diff --git a/csharp-book/book.toml b/csharp-book/book.toml index c2a1a7d..2c581a4 100644 --- a/csharp-book/book.toml +++ b/csharp-book/book.toml @@ -15,6 +15,7 @@ additional-js = ["mermaid.min.js", "mermaid-init.js"] [preprocessor.mermaid] command = "mdbook-mermaid" +optional = true [output.html.playground] editable = true diff --git a/csharp-book/src/ch05-data-structures-and-collections.md b/csharp-book/src/ch05-data-structures-and-collections.md index f98b0e7..b9fcf2c 100644 --- a/csharp-book/src/ch05-data-structures-and-collections.md +++ b/csharp-book/src/ch05-data-structures-and-collections.md @@ -283,6 +283,62 @@ fn print_string(s: &str) { } ``` +### Modern C#: Span\ and Inline Arrays + +C# has evolved beyond traditional arrays. `Span` provides type-safe, contiguous memory views that can live on the stack, while Inline Arrays (C# 12) offer fixed-size stack buffers. + +```csharp +// C# Span - view into contiguous memory +Span span = stackalloc int[] { 1, 2, 3, 4, 5 }; +span[0] = 10; + +ReadOnlySpan text = "Hello".AsSpan(); + +// Method accepting any contiguous memory view +void ProcessSpan(ReadOnlySpan data) +{ + for (int i = 0; i < data.Length; i++) + Console.WriteLine(data[i]); +} + +// Inline Arrays (C# 12) - fixed-size stack buffer +[InlineArray(5)] +struct IntBuffer +{ + private int _element; +} +``` + +```rust +// Rust &[T] / &mut [T] - borrowed view into contiguous memory +let mut array = [1, 2, 3, 4, 5]; +let slice: &mut [i32] = &mut array; +slice[0] = 10; + +let slice: &[i32] = &array; +let text: &str = "Hello"; + +// Function accepting any sequential data +fn process_slice(data: &[i32]) { + for (i, num) in data.iter().enumerate() { + println!("Index {}: {}", i, num); + } +} + +// Fixed-size arrays (stack allocated) +let buffer: [i32; 5] = [0; 5]; +``` + +| C# | Rust | +|----|------| +| `Span` (ref struct, stack-only) | `&mut [T]` / `&[T]` (borrowed slice) | +| `ReadOnlySpan` | `&[T]` (immutable slice) | +| `ReadOnlySpan` / `string.AsSpan()` | `&str` (string slice) | +| `[InlineArray(N)]` struct (C# 12) | `[T; N]` (fixed-size array) | +| `stackalloc T[]` with `Span` | `let arr: [T; N] = ...` (local array) | + +> **Key insight:** Rust's `&[T]` combines the role of C#'s `ArraySegment`, `Span`, and `ReadOnlySpan` — it's a fat pointer (pointer + length) that works with arrays, vectors, and subslices. C#'s Inline Arrays map naturally to Rust's `[T; N]` arrays, which are also stack-allocated by default. + *** ## Structs vs Classes diff --git a/rust-patterns-book/src/ch04-phantomdata-types-that-carry-no-data.md b/rust-patterns-book/src/ch04-phantomdata-types-that-carry-no-data.md index 2bc6ac0..3f1ac92 100644 --- a/rust-patterns-book/src/ch04-phantomdata-types-that-carry-no-data.md +++ b/rust-patterns-book/src/ch04-phantomdata-types-that-carry-no-data.md @@ -46,6 +46,7 @@ struct Slice<'a, T> { Use `PhantomData` to prevent mixing values from different "sessions" or "contexts": ```rust +use std::cell::RefCell; use std::marker::PhantomData; /// A handle that's valid only within a specific arena's lifetime @@ -55,33 +56,35 @@ struct ArenaHandle<'arena> { } struct Arena { - data: Vec, + data: RefCell>, } impl Arena { fn new() -> Self { - Arena { data: Vec::new() } + Arena { data: RefCell::new(Vec::new()) } } /// Allocate a string and return a branded handle - fn alloc<'a>(&'a mut self, value: String) -> ArenaHandle<'a> { - let index = self.data.len(); - self.data.push(value); + fn alloc(&self, value: String) -> ArenaHandle<'_> { + let mut data = self.data.borrow_mut(); + let index = data.len(); + data.push(value); ArenaHandle { index, _brand: PhantomData } } /// Look up by handle — only accepts handles from THIS arena - fn get<'a>(&'a self, handle: ArenaHandle<'a>) -> &'a str { - &self.data[handle.index] + fn get<'a>(&'a self, handle: ArenaHandle<'a>) -> String { + let data = self.data.borrow(); + data[handle.index].clone() } } fn main() { - let mut arena1 = Arena::new(); + let arena1 = Arena::new(); let handle1 = arena1.alloc("hello".to_string()); // Can't use handle1 with a different arena — lifetimes won't match - // let mut arena2 = Arena::new(); + // let arena2 = Arena::new(); // arena2.get(handle1); // ❌ Lifetime mismatch println!("{}", arena1.get(handle1)); // ✅