Introduction to Rust Arrays
Arrays in Rust are fixed-size collections of elements of the same type. They are stack-allocated and provide fast, contiguous memory access. Unlike vectors, arrays have a fixed length known at compile time, making them ideal for scenarios where you know the exact number of elements you need.
Key Concepts
- Fixed Size: Length is part of the type and known at compile time
- Stack Allocated: Stored on the stack, not heap
- Homogeneous: All elements must be the same type
- Zero-Cost: No runtime overhead compared to C arrays
- Type Safety: Bounds checking at runtime (in debug builds)
1. Array Basics
Declaration and Initialization
fn main() { // Explicit type annotation let arr1: [i32; 5] = [1, 2, 3, 4, 5]; // Type inference let arr2 = [1, 2, 3, 4, 5]; // Array with default values let zeros = [0; 10]; // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let threes = [3; 5]; // [3, 3, 3, 3, 3] // Mutable array let mut mutable_arr = [1, 2, 3, 4, 5]; mutable_arr[0] = 10; println!("arr1: {:?}", arr1); println!("zeros: {:?}", zeros); println!("mutable_arr: {:?}", mutable_arr); // Array with different initialization patterns let squares = [1, 4, 9, 16, 25]; let evens = [2, 4, 6, 8, 10]; } Array Type Signature
fn main() { // Array type is [T; N] where T is element type and N is length let arr: [i32; 5] = [1, 2, 3, 4, 5]; // Different sizes are different types let arr3: [i32; 3] = [1, 2, 3]; let arr5: [i32; 5] = [1, 2, 3, 4, 5]; // arr3 = arr5; // Error: mismatched types // Size is part of the type println!("Size of [i32; 5]: {} bytes", std::mem::size_of::<[i32; 5]>()); println!("Size of [i32; 10]: {} bytes", std::mem::size_of::<[i32; 10]>()); // Arrays implement common traits fn print_array<T: std::fmt::Debug>(arr: &[T]) { println!("{:?}", arr); } print_array(&arr); } 2. Accessing Array Elements
Indexing
fn main() { let arr = [10, 20, 30, 40, 50]; // Direct indexing let first = arr[0]; let third = arr[2]; let last = arr[4]; println!("First: {}, Third: {}, Last: {}", first, third, last); // Bounds checking (panics in debug, wraps in release) // let out_of_bounds = arr[10]; // This would panic // Safe access with get() match arr.get(2) { Some(value) => println!("Element at index 2: {}", value), None => println!("Index out of bounds"), } match arr.get(10) { Some(value) => println!("Element at index 10: {}", value), None => println!("Index 10 out of bounds"), // This prints } // Get with default let value = arr.get(2).unwrap_or(&-1); println!("Value: {}", value); // First and last elements if let Some(first) = arr.first() { println!("First: {}", first); } if let Some(last) = arr.last() { println!("Last: {}", last); } } Slicing Arrays
fn main() { let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // Full slice let full_slice = &arr[..]; println!("Full slice: {:?}", full_slice); // Partial slices let slice1 = &arr[2..5]; // [3, 4, 5] let slice2 = &arr[..4]; // [1, 2, 3, 4] let slice3 = &arr[6..]; // [7, 8, 9, 10] let slice4 = &arr[2..=5]; // [3, 4, 5, 6] (inclusive) println!("slice1: {:?}", slice1); println!("slice2: {:?}", slice2); println!("slice3: {:?}", slice3); println!("slice4: {:?}", slice4); // Mutable slices let mut mut_arr = [1, 2, 3, 4, 5]; let mut_slice = &mut mut_arr[1..4]; mut_slice[0] = 20; mut_slice[1] = 30; mut_slice[2] = 40; println!("Modified array: {:?}", mut_arr); // [1, 20, 30, 40, 5] // Slice from mutable array let slice = &mut mut_arr[..]; slice[0] = 100; println!("All modified: {:?}", mut_arr); // [100, 20, 30, 40, 5] } 3. Array Iteration
Basic Iteration
fn main() { let arr = [10, 20, 30, 40, 50]; // For loop over references (doesn't consume) for element in &arr { println!("Element: {}", element); } // For loop over values (consumes if not Copy) for element in arr { println!("Element: {}", element); } // println!("{:?}", arr); // Still works because i32 is Copy // Iterate with index for i in 0..arr.len() { println!("arr[{}] = {}", i, arr[i]); } // Using iter() method for element in arr.iter() { println!("iter(): {}", element); } // Mutable iteration let mut mut_arr = [1, 2, 3, 4, 5]; for element in mut_arr.iter_mut() { *element *= 2; } println!("After doubling: {:?}", mut_arr); } Advanced Iteration Techniques
fn main() { let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // Enumerate - get index and value for (i, value) in arr.iter().enumerate() { println!("arr[{}] = {}", i, value); } // Filter let evens: Vec<_> = arr.iter() .filter(|&&x| x % 2 == 0) .collect(); println!("Evens: {:?}", evens); // Map let doubled: Vec<_> = arr.iter() .map(|&x| x * 2) .collect(); println!("Doubled: {:?}", doubled); // Fold (reduce) let sum = arr.iter().fold(0, |acc, &x| acc + x); println!("Sum: {}", sum); // Any and All let has_negative = arr.iter().any(|&x| x < 0); let all_positive = arr.iter().all(|&x| x > 0); println!("Has negative: {}, All positive: {}", has_negative, all_positive); // Find if let Some(value) = arr.iter().find(|&&x| x == 5) { println!("Found: {}", value); } // Position if let Some(pos) = arr.iter().position(|&x| x == 7) { println!("7 found at index: {}", pos); } } 4. Array Methods and Operations
Basic Methods
fn main() { let arr = [1, 2, 3, 4, 5]; // Length println!("Length: {}", arr.len()); // Is empty println!("Is empty: {}", arr.is_empty()); // Check if contains println!("Contains 3: {}", arr.contains(&3)); println!("Contains 10: {}", arr.contains(&10)); // First and last println!("First: {:?}", arr.first()); println!("Last: {:?}", arr.last()); // Get specific element println!("Element at 2: {:?}", arr.get(2)); println!("Element at 10: {:?}", arr.get(10)); // Split array let (left, right) = arr.split_at(3); println!("Left: {:?}, Right: {:?}", left, right); // Windows for window in arr.windows(3) { println!("Window: {:?}", window); } // Chunks for chunk in arr.chunks(2) { println!("Chunk: {:?}", chunk); } // Chunks with remainder for chunk in arr.chunks(3) { println!("Chunk of 3: {:?}", chunk); } } Searching and Sorting
fn main() { let mut arr = [5, 2, 8, 1, 9, 3, 7, 4, 6]; // Sort arr.sort(); println!("Sorted: {:?}", arr); // Sort in reverse arr.sort_by(|a, b| b.cmp(a)); println!("Reverse sorted: {:?}", arr); // Binary search (requires sorted array) arr.sort(); match arr.binary_search(&5) { Ok(index) => println!("Found 5 at index {}", index), Err(index) => println!("5 not found, could be inserted at {}", index), } // Search with custom comparator let mut pairs = [(1, "one"), (3, "three"), (2, "two")]; pairs.sort_by(|a, b| a.0.cmp(&b.0)); println!("Sorted pairs: {:?}", pairs); // Check if sorted println!("Is sorted: {}", arr.is_sorted()); // Dedup (remove consecutive duplicates) let mut dup_arr = [1, 1, 2, 2, 3, 3, 4]; dup_arr.sort(); dup_arr.dedup(); println!("After dedup: {:?}", dup_arr); } Array Transformations
fn main() { let arr = [1, 2, 3, 4, 5]; // Reverse let reversed: Vec<_> = arr.iter().rev().collect(); println!("Reversed: {:?}", reversed); // Rotate left let rotate_left = [&arr[1..], &arr[..1]].concat(); println!("Rotate left: {:?}", rotate_left); // Rotate right let rotate_right = [&arr[arr.len()-1..], &arr[..arr.len()-1]].concat(); println!("Rotate right: {:?}", rotate_right); // Fill with value let mut fill_arr = [0; 5]; fill_arr.fill(42); println!("Filled: {:?}", fill_arr); // Fill with range for (i, val) in fill_arr.iter_mut().enumerate() { *val = i * 10; } println!("Range fill: {:?}", fill_arr); // Map to new array (using const generics) fn map_array<T, U, const N: usize>(arr: [T; N], mut f: impl FnMut(T) -> U) -> [U; N] { let mut result: [std::mem::MaybeUninit<U>; N] = unsafe { std::mem::MaybeUninit::uninit().assume_init() }; for (i, x) in arr.into_iter().enumerate() { result[i].write(f(x)); } unsafe { std::mem::transmute_copy(&result) } } let doubled = map_array(arr, |x| x * 2); println!("Doubled array: {:?}", doubled); } 5. Multidimensional Arrays
2D Arrays
fn main() { // 2D array declaration let matrix: [[i32; 3]; 3] = [ [1, 2, 3], [4, 5, 6], [7, 8, 9], ]; // Access elements println!("matrix[1][1] = {}", matrix[1][1]); // 5 // Iterate over 2D array for row in &matrix { for element in row { print!("{} ", element); } println!(); } // Mutable 2D array let mut mut_matrix = [[0; 3]; 3]; for i in 0..3 { for j in 0..3 { mut_matrix[i][j] = (i * 3 + j + 1) as i32; } } println!("Mut matrix: {:?}", mut_matrix); // Jagged arrays (using Vec for different lengths) let jagged: [Vec<i32>; 3] = [ vec![1, 2], vec![3, 4, 5], vec![6, 7, 8, 9], ]; for (i, row) in jagged.iter().enumerate() { println!("Row {}: {:?}", i, row); } } 3D Arrays
fn main() { // 3D array let cube: [[[i32; 2]; 2]; 2] = [ [[1, 2], [3, 4]], [[5, 6], [7, 8]], ]; println!("cube[0][0][0] = {}", cube[0][0][0]); // 1 println!("cube[1][1][1] = {}", cube[1][1][1]); // 8 // Iterate through 3D for (i, layer) in cube.iter().enumerate() { println!("Layer {}:", i); for (j, row) in layer.iter().enumerate() { print!(" Row {}: ", j); for (k, &val) in row.iter().enumerate() { print!("{} ", val); } println!(); } } // Create 3D array dynamically let mut dynamic_cube = [[[0; 3]; 3]; 3]; for i in 0..3 { for j in 0..3 { for k in 0..3 { dynamic_cube[i][j][k] = (i * 9 + j * 3 + k) as i32; } } } println!("Dynamic cube: {:?}", dynamic_cube); } Matrix Operations
fn main() { // Matrix addition let a = [[1, 2], [3, 4]]; let b = [[5, 6], [7, 8]]; let mut c = [[0; 2]; 2]; for i in 0..2 { for j in 0..2 { c[i][j] = a[i][j] + b[i][j]; } } println!("Matrix A + B = {:?}", c); // Matrix multiplication let a = [[1, 2], [3, 4]]; let b = [[5, 6], [7, 8]]; let mut d = [[0; 2]; 2]; for i in 0..2 { for j in 0..2 { for k in 0..2 { d[i][j] += a[i][k] * b[k][j]; } } } println!("Matrix A * B = {:?}", d); // Transpose let mut transpose = [[0; 2]; 2]; for i in 0..2 { for j in 0..2 { transpose[j][i] = a[i][j]; } } println!("Transpose of A = {:?}", transpose); } 6. Arrays and Functions
Passing Arrays to Functions
// Pass by value (copy for Copy types, move otherwise) fn process_array(mut arr: [i32; 5]) -> [i32; 5] { for i in 0..arr.len() { arr[i] *= 2; } arr } // Pass by reference (immutable) fn print_array(arr: &[i32; 5]) { println!("Array: {:?}", arr); } // Pass by mutable reference fn modify_array(arr: &mut [i32; 5]) { for i in 0..arr.len() { arr[i] += 10; } } // Pass slice (most flexible for functions) fn sum_slice(arr: &[i32]) -> i32 { arr.iter().sum() } // Generic array function fn print_any_array<T: std::fmt::Debug, const N: usize>(arr: &[T; N]) { println!("Array of length {}: {:?}", N, arr); } fn main() { let arr = [1, 2, 3, 4, 5]; // Pass by reference print_array(&arr); // Pass by value (returns modified copy) let doubled = process_array(arr); println!("Original: {:?}, Doubled: {:?}", arr, doubled); // Pass by mutable reference let mut mut_arr = [1, 2, 3, 4, 5]; modify_array(&mut mut_arr); println!("Modified: {:?}", mut_arr); // Pass as slice let sum = sum_slice(&arr); println!("Sum: {}", sum); // Generic function print_any_array(&arr); print_any_array(&['a', 'b', 'c']); } Returning Arrays from Functions
// Return fixed-size array fn create_array() -> [i32; 5] { [1, 2, 3, 4, 5] } // Return array from computation fn generate_squares(n: usize) -> [i32; 5] { let mut arr = [0; 5]; for i in 0..5 { arr[i] = ((i + 1) * (i + 1)) as i32; } arr } // Using const generics to return arrays of any size fn fill_array<const N: usize>(value: i32) -> [i32; N] { [value; N]
} // Return array from Option fn maybe_array(flag: bool) -> Option<[i32; 3]> { if flag { Some([1, 2, 3]) } else { None } } fn main() { let arr1 = create_array(); println!("Created: {:?}", arr1); let squares = generate_squares(5); println!("Squares: {:?}", squares); let tens: [i32; 10] = fill_array(10); println!("Tens: {:?}", tens); match maybe_array(true) { Some(arr) => println!("Got array: {:?}", arr), None => println!("No array"), } }
7. Arrays and Generics
Const Generics
// Function that works with arrays of any size fn sum_array<T: std::ops::Add<Output = T> + Default + Copy, const N: usize>(arr: [T; N]) -> T { let mut sum = T::default(); for i in 0..N { sum = sum + arr[i]; } sum } // Generic array struct struct ArrayWrapper<T, const N: usize> { data: [T; N], } impl<T: std::fmt::Debug, const N: usize> ArrayWrapper<T, N> { fn new(data: [T; N]) -> Self { ArrayWrapper { data } } fn get(&self, index: usize) -> Option<&T> { self.data.get(index) } fn len(&self) -> usize { N } } // Const generic with bounds fn compare_arrays<T: PartialEq, const N: usize>(a: &[T; N], b: &[T; N]) -> bool { a == b } fn main() { let arr1 = [1, 2, 3, 4, 5]; let arr2 = [1, 2, 3, 4, 5]; let arr3 = [1, 2, 3, 4, 6]; println!("Sum of arr1: {}", sum_array(arr1)); let sum_float = sum_array([1.1, 2.2, 3.3]); println!("Sum of floats: {}", sum_float); let wrapper = ArrayWrapper::new([10, 20, 30, 40]); println!("Wrapper length: {}", wrapper.len()); println!("Element at 2: {:?}", wrapper.get(2)); println!("arr1 == arr2: {}", compare_arrays(&arr1, &arr2)); println!("arr1 == arr3: {}", compare_arrays(&arr1, &arr3)); } Arrays in Traits
trait ArrayOperations<T, const N: usize> { fn sum(&self) -> T; fn product(&self) -> T; fn average(&self) -> f64; } impl<T, const N: usize> ArrayOperations<T, N> for [T; N] where T: std::ops::Add<Output = T> + std::ops::Mul<Output = T> + Default + Copy, f64: From<T>, { fn sum(&self) -> T { let mut sum = T::default(); for i in 0..N { sum = sum + self[i]; } sum } fn product(&self) -> T { let mut product = self[0]; for i in 1..N { product = product * self[i]; } product } fn average(&self) -> f64 { let sum: f64 = self.sum().into(); sum / (N as f64) } } fn main() { let numbers = [1, 2, 3, 4, 5]; println!("Sum: {}", numbers.sum()); println!("Product: {}", numbers.product()); println!("Average: {}", numbers.average()); let floats = [1.5, 2.5, 3.5, 4.5]; println!("Float sum: {}", floats.sum()); println!("Float average: {}", floats.average()); } 8. Array Performance
Stack vs Heap
fn main() { // Stack allocation (fast) let stack_array = [0; 1000]; // Heap allocation via Box (still need to copy) let heap_array = Box::new([0; 1000]); // Vector (heap allocated, dynamic) let vec = vec![0; 1000]; println!("Stack array size: {} bytes", std::mem::size_of_val(&stack_array)); println!("Boxed array size: {} bytes", std::mem::size_of_val(&heap_array)); println!("Vector size: {} bytes", std::mem::size_of_val(&vec)); // Performance comparison use std::time::Instant; // Stack array access let start = Instant::now(); let mut sum_stack = 0; for i in 0..1000 { sum_stack += stack_array[i]; } println!("Stack access time: {:?}", start.elapsed()); // Heap array access let start = Instant::now(); let mut sum_heap = 0; for i in 0..1000 { sum_heap += heap_array[i]; } println!("Heap access time: {:?}", start.elapsed()); } Cache Efficiency
fn main() { // Cache-friendly sequential access let arr = [0; 10000]; let start = std::time::Instant::now(); for i in 0..10000 { let _ = arr[i]; } println!("Sequential access: {:?}", start.elapsed()); // Cache-unfriendly random access (simulated) let start = std::time::Instant::now(); for i in 0..10000 { let idx = (i * 997) % 10000; // Non-sequential pattern let _ = arr[idx]; } println!("Random access: {:?}", start.elapsed()); // Row-major vs column-major for 2D let matrix = [[0; 1000]; 1000]; // Row-major access (cache-friendly) let start = std::time::Instant::now(); for i in 0..1000 { for j in 0..1000 { let _ = matrix[i][j]; } } println!("Row-major: {:?}", start.elapsed()); // Column-major access (cache-unfriendly) let start = std::time::Instant::now(); for j in 0..1000 { for i in 0..1000 { let _ = matrix[i][j]; } } println!("Column-major: {:?}", start.elapsed()); } 9. Common Array Patterns
Sliding Window
fn main() { let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // Simple sliding window for window in arr.windows(3) { println!("Window: {:?}", window); } // Moving average let moving_avg: Vec<f64> = arr.windows(3) .map(|w| w.iter().sum::<i32>() as f64 / 3.0) .collect(); println!("Moving average: {:?}", moving_avg); // Max in each window let max_in_window: Vec<_> = arr.windows(4) .map(|w| w.iter().max().unwrap()) .collect(); println!("Max in window: {:?}", max_in_window); // Overlapping windows with stride for chunk in arr.chunks(3) { println!("Chunk: {:?}", chunk); } // Custom sliding window with step let step = 2; for i in (0..arr.len()).step_by(step) { if i + 3 <= arr.len() { println!("Window at {}: {:?}", i, &arr[i..i+3]); } } } Two-Pointer Technique
fn main() { // Find pair that sums to target let mut arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; arr.sort(); let target = 10; let mut left = 0; let mut right = arr.len() - 1; while left < right { let sum = arr[left] + arr[right]; if sum == target { println!("Found pair: {} + {} = {}", arr[left], arr[right], target); left += 1; right -= 1; } else if sum < target { left += 1; } else { right -= 1; } } // Reverse array in place let mut arr = [1, 2, 3, 4, 5]; let mut left = 0; let mut right = arr.len() - 1; while left < right { arr.swap(left, right); left += 1; right -= 1; } println!("Reversed: {:?}", arr); // Remove duplicates from sorted array let mut sorted = [1, 1, 2, 2, 3, 3, 4, 4, 5]; let mut write_pos = 1; for read_pos in 1..sorted.len() { if sorted[read_pos] != sorted[write_pos - 1] { sorted[write_pos] = sorted[read_pos]; write_pos += 1; } } println!("After removing duplicates: {:?}", &sorted[..write_pos]); } Binary Search
fn main() { let arr = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]; // Binary search implementation fn binary_search<T: Ord>(arr: &[T], target: &T) -> Option<usize> { let mut left = 0; let mut right = arr.len(); while left < right { let mid = left + (right - left) / 2; match arr[mid].cmp(target) { std::cmp::Ordering::Equal => return Some(mid), std::cmp::Ordering::Less => left = mid + 1, std::cmp::Ordering::Greater => right = mid, } } None } for &target in &[5, 10, 15] { match binary_search(&arr, &target) { Some(pos) => println!("Found {} at index {}", target, pos), None => println!("{} not found", target), } } // Find first element greater than target let target = 8; let result = arr.iter().position(|&x| x > target); println!("First element > {} is at index {:?}", target, result); // Find range of equal elements let duplicates = [1, 2, 2, 2, 3, 4, 4, 5]; let target = 2; let start = duplicates.iter().position(|&x| x == target); let end = duplicates.iter().rposition(|&x| x == target); match (start, end) { (Some(s), Some(e)) => println!("Range of {}: [{}, {}]", target, s, e), _ => println!("{} not found", target), } } 10. Arrays and Pattern Matching
Destructuring Arrays
fn main() { // Basic destructuring let arr = [1, 2, 3, 4, 5]; let [a, b, c, d, e] = arr; println!("a: {}, b: {}, c: {}, d: {}, e: {}", a, b, c, d, e); // Ignoring elements with .. let [first, second, ..] = arr; println!("First: {}, Second: {}", first, second); let [.., last] = arr; println!("Last: {}", last); let [first, .., last] = arr; println!("First: {}, Last: {}", first, last); let [first, second, middle @ .., fourth, fifth] = arr; println!("First: {}, Second: {}, Middle: {:?}, Fourth: {}, Fifth: {}", first, second, middle, fourth, fifth); // Destructuring in match match arr { [1, 2, ..] => println!("Starts with 1,2"), [x, y, z, ..] if x < y && y < z => println!("Increasing start"), [a, b, c, d, e] if a + e == b + d => println!("Symmetrical"), _ => println!("Other pattern"), } } Pattern Matching with Arrays
fn main() { // Match on array length and content fn analyze_array(arr: &[i32]) { match arr { [] => println!("Empty array"), [x] => println!("Single element: {}", x), [x, y] => println!("Two elements: {}, {}", x, y), [x, y, z] => println!("Three elements: {}, {}, {}", x, y, z), [x, .., y] => println!("First: {}, Last: {}, and {} in between", x, y, arr.len() - 2), _ => println!("Array with {} elements", arr.len()), } } analyze_array(&[]); analyze_array(&[1]); analyze_array(&[1, 2]); analyze_array(&[1, 2, 3]); analyze_array(&[1, 2, 3, 4, 5]); // Pattern matching with guards let arr = [1, 3, 5, 7, 9]; match arr { [x, y, ..] if x < y => println!("Increasing start"), [x, y, ..] if x > y => println!("Decreasing start"), _ => println!("Other pattern"), } // Extract based on pattern let arr = [Some(1), Some(2), Some(3), None, Some(5)]; if let [Some(x), Some(y), ..] = arr { println!("First two are Some: {}, {}", x, y); } } 11. Arrays and Serialization
JSON Serialization
use serde::{Serialize, Deserialize}; use serde_json; #[derive(Debug, Serialize, Deserialize)] struct Data { name: String, values: [i32; 5], matrix: [[i32; 2]; 2], } fn main() -> Result<(), Box<dyn std::error::Error>> { let data = Data { name: "Test".to_string(), values: [1, 2, 3, 4, 5], matrix: [[1, 2], [3, 4]], }; // Serialize to JSON let json = serde_json::to_string_pretty(&data)?; println!("Serialized: {}", json); // Deserialize from JSON let deserialized: Data = serde_json::from_str(&json)?; println!("Deserialized: {:?}", deserialized); // Working with raw JSON arrays let json_array = "[1, 2, 3, 4, 5]"; let array: [i32; 5] = serde_json::from_str(json_array)?; println!("Array from JSON: {:?}", array); // 2D array from JSON let json_matrix = "[[1,2],[3,4]]"; let matrix: [[i32; 2]; 2] = serde_json::from_str(json_matrix)?; println!("Matrix from JSON: {:?}", matrix); Ok(()) } Binary Serialization
use byteorder::{LittleEndian, WriteBytesExt, ReadBytesExt}; use std::io::Cursor; fn main() -> Result<(), Box<dyn std::error::Error>> { let arr = [1, 2, 3, 4, 5]; // Write array to bytes let mut bytes = Vec::new(); for &num in &arr { bytes.write_i32::<LittleEndian>(num)?; } println!("Binary representation: {:?}", bytes); // Read array from bytes let mut cursor = Cursor::new(bytes); let mut read_arr = [0; 5]; for i in 0..5 { read_arr[i] = cursor.read_i32::<LittleEndian>()?; } println!("Read back: {:?}", read_arr); // Using bytemuck for zero-copy use bytemuck::{Pod, Zeroable, cast_slice, from_bytes}; #[repr(C)] #[derive(Debug, Copy, Clone, Zeroable, Pod)] struct Color { r: u8, g: u8, b: u8, } let colors = [ Color { r: 255, g: 0, b: 0 }, Color { r: 0, g: 255, b: 0 }, Color { r: 0, g: 0, b: 255 }, ]; // Cast to byte slice let bytes = cast_slice::<Color, u8>(&colors); println!("Colors as bytes: {:?}", bytes); // Cast back let colors2: &[Color; 3] = from_bytes(&bytes[..std::mem::size_of::<[Color; 3]>()]); println!("Colors back: {:?}", colors2); Ok(()) } 12. Advanced Array Techniques
Zero-Copy Views
use std::mem; #[repr(C)] #[derive(Debug, Copy, Clone)] struct Pixel { r: u8, g: u8, b: u8, a: u8, } fn main() { // Raw bytes let raw_data: [u8; 16] = [ 255, 0, 0, 255, // Red pixel 0, 255, 0, 255, // Green pixel 0, 0, 255, 255, // Blue pixel 255, 255, 255, 255, // White pixel ]; // Zero-cast to Pixel array let pixels: &[Pixel; 4] = unsafe { &*(&raw_data as *const _ as *const [Pixel; 4]) }; println!("Pixels: {:?}", pixels); // Work with pixels directly for (i, pixel) in pixels.iter().enumerate() { println!("Pixel {}: RGB({}, {}, {})", i, pixel.r, pixel.g, pixel.b); } // Convert back let back_to_bytes: &[u8; 16] = unsafe { &*(pixels as *const _ as *const [u8; 16]) }; println!("Back to bytes: {:?}", back_to_bytes); } SIMD Operations
// Requires nightly Rust and feature flags #![cfg_attr(feature = "nightly", feature(portable_simd))] #[cfg(feature = "nightly")] use std::simd::{f32x4, SimdFloat}; #[cfg(feature = "nightly")] fn simd_example() { let a = [1.0, 2.0, 3.0, 4.0]; let b = [5.0, 6.0, 7.0, 8.0]; // Load into SIMD vectors let a_vec = f32x4::from_array(a); let b_vec = f32x4::from_array(b); // SIMD operations let sum = a_vec + b_vec; let product = a_vec * b_vec; println!("SIMD sum: {:?}", sum.to_array()); println!("SIMD product: {:?}", product.to_array()); // Dot product let dot = (a_vec * b_vec).reduce_sum(); println!("Dot product: {}", dot); } #[cfg(not(feature = "nightly"))] fn simd_example() { println!("SIMD requires nightly Rust"); } fn main() { simd_example(); } Array Pools and Reuse
struct ArrayPool<T, const N: usize> { pools: Vec<[T; N]>, } impl<T: Default + Copy, const N: usize> ArrayPool<T, N> { fn new() -> Self { ArrayPool { pools: Vec::new() } } fn acquire(&mut self) -> [T; N] { self.pools.pop().unwrap_or([T::default(); N]) } fn release(&mut self, mut arr: [T; N]) { // Reset before returning to pool for elem in &mut arr { *elem = T::default(); } self.pools.push(arr); } } fn main() { let mut pool = ArrayPool::<i32, 1024>::new(); // Get arrays from pool let mut arr1 = pool.acquire(); let mut arr2 = pool.acquire(); // Use them arr1[0] = 42; arr2[511] = 100; println!("arr1[0]: {}, arr2[511]: {}", arr1[0], arr2[511]); // Return to pool pool.release(arr1); pool.release(arr2); println!("Pool size: {}", pool.pools.len()); // Reuse let arr3 = pool.acquire(); println!("arr3[0] after reuse: {}", arr3[0]); // 0 (reset) } Conclusion
Rust arrays provide a powerful, efficient way to work with fixed-size collections:
Key Takeaways
- Fixed Size: Length is part of the type system
- Stack Allocation: Fast access, no heap overhead
- Type Safety: Bounds checking prevents buffer overflows
- Pattern Matching: Destructuring and matching support
- Const Generics: Generic code for arrays of any size
- Zero-Cost: Compiles to efficient machine code
Array vs Vector Comparison
| Feature | Array | Vector |
|---|---|---|
| Size | Fixed at compile time | Dynamic |
| Allocation | Stack | Heap |
| Type | [T; N] | Vec<T> |
| Resizable | No | Yes |
| Performance | Faster access | Slightly slower |
| Use case | Known size | Unknown/Changing size |
Best Practices
- Use arrays when you know the size at compile time
- Prefer slices for function parameters
- Use const generics for generic array code
- Be careful with bounds - use
get()for safe access - Consider cache efficiency for large arrays
- Profile before optimizing array access patterns
Common Operations Cheat Sheet
// Creation let arr = [1, 2, 3, 4, 5]; let zeros = [0; 100]; let mut mut_arr = [1, 2, 3]; // Access let first = arr[0]; let safe = arr.get(2); let slice = &arr[1..4]; // Information let len = arr.len(); let is_empty = arr.is_empty(); // Iteration for item in &arr { } for item in arr.iter() { } for item in arr.iter_mut() { } for (i, item) in arr.iter().enumerate() { } // Search let contains = arr.contains(&3); let position = arr.iter().position(|&x| x == 3); // Transformation let doubled: Vec<_> = arr.iter().map(|&x| x * 2).collect(); let sum: i32 = arr.iter().sum(); // Modification let mut arr = [1, 2, 3]; arr[0] = 10; arr.swap(1, 2); arr.reverse(); arr.sort(); Rust arrays combine the performance of C arrays with the safety of Rust's type system, making them ideal for systems programming, embedded development, and any scenario where fixed-size collections are needed.