Introduction to Vectors in Rust
Vectors (Vec<T>) are one of the most commonly used collections in Rust. They provide a dynamic, growable array that stores elements of the same type in contiguous memory. Vectors are heap-allocated and offer flexible sizing while maintaining fast access times.
Key Concepts
- Dynamic Sizing: Grows and shrinks at runtime
- Type Safety: All elements must be the same type
- Memory Contiguous: Elements stored sequentially in memory
- Ownership: Vector owns its elements
- Performance: O(1) index access, amortized O(1) push
1. Creating Vectors
Basic Creation Methods
fn main() { // Method 1: Vec::new() - empty vector let mut v1: Vec<i32> = Vec::new(); v1.push(1); v1.push(2); println!("v1: {:?}", v1); // Method 2: vec! macro - with initial values let v2 = vec![1, 2, 3, 4, 5]; println!("v2: {:?}", v2); // Method 3: vec! macro with repeated value let v3 = vec![0; 10]; // Creates vector with 10 zeros println!("v3: {:?}", v3); // Method 4: From iterator let v4: Vec<i32> = (0..5).collect(); println!("v4: {:?}", v4); // Method 5: From array let arr = [1, 2, 3, 4, 5]; let v5 = Vec::from(arr); println!("v5: {:?}", v5); // Method 6: with_capacity - pre-allocate space let mut v6 = Vec::with_capacity(10); println!("v6 capacity: {}, length: {}", v6.capacity(), v6.len()); // Method 7: From raw parts (advanced, unsafe) let ptr = Box::into_raw(Box::new([1, 2, 3])) as *mut i32; let v7 = unsafe { Vec::from_raw_parts(ptr, 3, 3) }; println!("v7: {:?}", v7); } Type Annotations
fn main() { // Explicit type annotation let v1: Vec<i32> = Vec::new(); let v2: Vec<&str> = Vec::new(); let v3: Vec<String> = Vec::new(); // Type inference from usage let mut v4 = Vec::new(); v4.push(42); // Now v4 is Vec<i32> let mut v5 = Vec::new(); v5.push("hello"); // Now v5 is Vec<&str> // Using turbofish syntax let v6 = Vec::<i32>::new(); let v7 = vec![1, 2, 3]; // Vec<i32> inferred // Complex types let v8: Vec<Vec<i32>> = vec![ vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9], ]; println!("v8: {:?}", v8); } 2. Accessing Elements
Indexing and Safe Access
fn main() { let v = vec![10, 20, 30, 40, 50]; // Direct indexing (panics if out of bounds) let third = v[2]; println!("Third element: {}", third); // Safe access with get() (returns Option) match v.get(2) { Some(value) => println!("Third element safely: {}", value), None => println!("No third element"), } // Safe access to non-existent index match v.get(10) { Some(value) => println!("Element at 10: {}", value), None => println!("No element at index 10"), } // First and last elements if let Some(first) = v.first() { println!("First: {}", first); } if let Some(last) = v.last() { println!("Last: {}", last); } // Getting slices let slice = &v[1..4]; // [20, 30, 40] println!("Slice: {:?}", slice); // Getting mutable references let mut v = vec![1, 2, 3, 4, 5]; if let Some(element) = v.get_mut(2) { *element = 100; } println!("After modification: {:?}", v); } Multiple Element Access
fn main() { let v = vec![1, 2, 3, 4, 5]; // Get multiple elements let first_two = &v[0..2]; println!("First two: {:?}", first_two); // Get all except first let tail = &v[1..]; println!("Tail: {:?}", tail); // Get all except last let init = &v[..v.len()-1]; println!("Init: {:?}", init); // Split into two at index let (left, right) = v.split_at(3); println!("Left: {:?}, Right: {:?}", left, right); // Chunks let chunks: Vec<&[i32]> = v.chunks(2).collect(); println!("Chunks of 2: {:?}", chunks); // Windows (overlapping chunks) let windows: Vec<&[i32]> = v.windows(3).collect(); println!("Windows of 3: {:?}", windows); } 3. Modifying Vectors
Adding Elements
fn main() { // push - add to end let mut v = Vec::new(); v.push(1); v.push(2); v.push(3); println!("After push: {:?}", v); // insert at position v.insert(1, 10); // Insert 10 at index 1 println!("After insert: {:?}", v); // append - move all elements from another vector let mut v2 = vec![4, 5, 6]; v.append(&mut v2); println!("After append: {:?}, v2 empty: {:?}", v, v2); // extend - add elements from iterator v.extend(vec![7, 8, 9]); println!("After extend: {:?}", v); // extend from slice v.extend_from_slice(&[10, 11, 12]); println!("After extend_from_slice: {:?}", v); // reserve space let mut v = Vec::with_capacity(2); println!("Capacity: {}", v.capacity()); v.reserve(10); // Ensure capacity for at least 10 more println!("Capacity after reserve: {}", v.capacity()); } Removing Elements
fn main() { let mut v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // pop - remove last element if let Some(last) = v.pop() { println!("Popped: {}", last); } println!("After pop: {:?}", v); // remove at index let removed = v.remove(2); // Remove element at index 2 println!("Removed at index 2: {}", removed); println!("After remove: {:?}", v); // remove with swap (doesn't preserve order but O(1)) let mut v = vec![1, 2, 3, 4, 5]; let removed = v.swap_remove(1); // Remove element at index 1, replace with last println!("Swap removed: {}, vector: {:?}", removed, v); // truncate to length let mut v = vec![1, 2, 3, 4, 5]; v.truncate(3); println!("Truncated to 3: {:?}", v); // clear all elements v.clear(); println!("Cleared: {:?}, len: {}", v, v.len()); // drain - remove range let mut v = vec![1, 2, 3, 4, 5]; let drained: Vec<_> = v.drain(1..4).collect(); println!("Drained: {:?}, remaining: {:?}", drained, v); // retain - keep elements satisfying condition let mut v = vec![1, 2, 3, 4, 5, 6]; v.retain(|&x| x % 2 == 0); println!("After retain (evens): {:?}", v); // dedup - remove consecutive duplicates let mut v = vec![1, 1, 2, 2, 3, 1, 1, 4]; v.dedup(); println!("After dedup: {:?}", v); // [1, 2, 3, 1, 4] } Modifying Elements
fn main() { // Modify by index let mut v = vec![1, 2, 3, 4, 5]; v[2] = 30; println!("After modification: {:?}", v); // Modify using get_mut if let Some(value) = v.get_mut(3) { *value = 40; } println!("After get_mut: {:?}", v); // Swap elements v.swap(0, 4); // Swap first and last println!("After swap: {:?}", v); // Replace with new value, return old let old = std::mem::replace(&mut v[1], 100); println!("Replaced {} with 100, vector: {:?}", old, v); // Update all elements with map in place for x in &mut v { *x *= 2; } println!("After doubling: {:?}", v); // Using resize to change length with default value let mut v = vec![1, 2, 3]; v.resize(5, 0); println!("After resize to 5: {:?}", v); v.resize(2, 0); println!("After resize to 2: {:?}", v); // resize with custom default let mut v = vec!["a", "b", "c"]; v.resize_with(5, || "default"); println!("After resize_with: {:?}", v); } 4. Iterating Over Vectors
Different Iteration Methods
fn main() { let v = vec![10, 20, 30, 40, 50]; // Immutable iteration println!("Immutable iteration:"); for value in &v { print!("{} ", value); } println!(); // Mutable iteration println!("Mutable iteration:"); let mut v = vec![1, 2, 3, 4, 5]; for value in &mut v { *value *= 2; print!("{} ", value); } println!("\nAfter mutation: {:?}", v); // By value (consumes vector) let v = vec![1, 2, 3]; for value in v { print!("{} ", value); } println!(); // println!("{:?}", v); // Error: v moved // With index (enumerate) let v = vec![10, 20, 30]; for (index, value) in v.iter().enumerate() { println!("Index {}: {}", index, value); } // Using iterators explicitly let v = vec![1, 2, 3]; let mut iter = v.iter(); while let Some(value) = iter.next() { println!("Iterator next: {}", value); } } Functional Iteration
fn main() { let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // map let doubled: Vec<_> = numbers.iter().map(|&x| x * 2).collect(); println!("Doubled: {:?}", doubled); // filter let evens: Vec<_> = numbers.iter().filter(|&&x| x % 2 == 0).collect(); println!("Evens: {:?}", evens); // filter_map let strings = vec!["1", "2", "abc", "3", "def"]; let parsed: Vec<i32> = strings.iter() .filter_map(|s| s.parse().ok()) .collect(); println!("Parsed numbers: {:?}", parsed); // fold (reduce) let sum = numbers.iter().fold(0, |acc, &x| acc + x); println!("Sum: {}", sum); // any and all let has_even = numbers.iter().any(|&x| x % 2 == 0); let all_positive = numbers.iter().all(|&x| x > 0); println!("Has even: {}, All positive: {}", has_even, all_positive); // find and position if let Some(first_even) = numbers.iter().find(|&&x| x % 2 == 0) { println!("First even: {}", first_even); } if let Some(pos) = numbers.iter().position(|&x| x == 5) { println!("5 at position: {}", pos); } // min and max println!("Min: {:?}", numbers.iter().min()); println!("Max: {:?}", numbers.iter().max()); // chain operations let result: i32 = numbers.iter() .filter(|&&x| x % 2 == 0) .map(|&x| x * x) .take(3) .sum(); println!("Sum of first 3 even squares: {}", result); } 5. Vector Capacity and Performance
Understanding Capacity
fn main() { // Capacity vs length let mut v = Vec::with_capacity(10); println!("Initial - len: {}, cap: {}", v.len(), v.capacity()); for i in 0..5 { v.push(i); println!("After push {} - len: {}, cap: {}", i, v.len(), v.capacity()); } // When capacity is exceeded, vector reallocates for i in 5..15 { v.push(i); println!("After push {} - len: {}, cap: {}", i, v.len(), v.capacity()); } // Shrink to fit v.shrink_to_fit(); println!("After shrink - len: {}, cap: {}", v.len(), v.capacity()); // Reserve additional capacity v.reserve(100); println!("After reserve 100 - cap: {}", v.capacity()); // Reserve exact capacity v.reserve_exact(50); println!("After reserve_exact 50 - cap: {}", v.capacity()); } Performance Comparisons
use std::time::Instant; fn main() { // Pre-allocation vs dynamic growth const SIZE: usize = 100_000; // Without pre-allocation let start = Instant::now(); let mut v1 = Vec::new(); for i in 0..SIZE { v1.push(i); } println!("Without pre-allocation: {:?}", start.elapsed()); // With pre-allocation let start = Instant::now(); let mut v2 = Vec::with_capacity(SIZE); for i in 0..SIZE { v2.push(i); } println!("With pre-allocation: {:?}", start.elapsed()); // Index access vs get() let v = vec![0; SIZE]; let start = Instant::now(); for i in 0..SIZE { let _ = v[i]; } println!("Direct indexing: {:?}", start.elapsed()); let start = Instant::now(); for i in 0..SIZE { let _ = v.get(i); } println!("get() method: {:?}", start.elapsed()); // Iteration methods let start = Instant::now(); let mut sum = 0; for i in 0..SIZE { sum += v[i]; } println!("Manual index loop: {:?}", start.elapsed()); let start = Instant::now(); let mut sum = 0; for &x in &v { sum += x; } println!("Iterator loop: {:?}", start.elapsed()); } 6. Working with Different Types
Vectors of Custom Types
#[derive(Debug, Clone)] struct Person { name: String, age: u32, } #[derive(Debug)] struct Point { x: f64, y: f64, } impl Point { fn new(x: f64, y: f64) -> Self { Point { x, y } } fn distance(&self, other: &Point) -> f64 { let dx = self.x - other.x; let dy = self.y - other.y; (dx * dx + dy * dy).sqrt() } } fn main() { // Vector of structs let people = vec![ Person { name: "Alice".to_string(), age: 30 }, Person { name: "Bob".to_string(), age: 25 }, Person { name: "Charlie".to_string(), age: 35 }, ]; for person in &people { println!("{:?}", person); } // Vector of Points let points = vec![ Point::new(0.0, 0.0), Point::new(3.0, 4.0), Point::new(1.0, 1.0), ]; let origin = &points[0]; for point in &points[1..] { println!("Distance from origin: {}", origin.distance(point)); } // Sorting custom types (need PartialOrd) let mut people = people; people.sort_by(|a, b| a.age.cmp(&b.age)); println!("Sorted by age: {:?}", people); // Vector of enums (heterogeneous data) #[derive(Debug)] enum Value { Int(i32), Float(f64), Text(String), } let mixed = vec![ Value::Int(42), Value::Float(3.14), Value::Text("hello".to_string()), Value::Int(100), ]; for value in &mixed { match value { Value::Int(x) => println!("Int: {}", x), Value::Float(x) => println!("Float: {}", x), Value::Text(s) => println!("Text: {}", s), } } } Vectors of References
fn main() { // Vector of references let x = 10; let y = 20; let z = 30; let refs = vec![&x, &y, &z]; for &&val in &refs { println!("Referenced value: {}", val); } // Vector of references to strings let strings = vec![ String::from("hello"), String::from("world"), String::from("!"), ]; let refs: Vec<&String> = strings.iter().collect(); for s in refs { println!("{}", s); } // Vector of boxed values let boxed: Vec<Box<i32>> = vec![Box::new(1), Box::new(2), Box::new(3)]; for b in &boxed { println!("Boxed: {}", **b); } // Vector of trait objects trait Animal { fn speak(&self); } struct Dog; struct Cat; impl Animal for Dog { fn speak(&self) { println!("Woof!"); } } impl Animal for Cat { fn speak(&self) { println!("Meow!"); } } let animals: Vec<Box<dyn Animal>> = vec![ Box::new(Dog), Box::new(Cat), Box::new(Dog), ]; for animal in &animals { animal.speak(); } } 7. Vector Slices and Views
Working with Slices
fn main() { let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // Create slices let slice1 = &v[2..5]; // [3, 4, 5] let slice2 = &v[..4]; // [1, 2, 3, 4] let slice3 = &v[6..]; // [7, 8, 9, 10] let slice4 = &v[..]; // entire vector println!("slice1: {:?}", slice1); println!("slice2: {:?}", slice2); println!("slice3: {:?}", slice3); // Functions that work with slices fn sum_slice(slice: &[i32]) -> i32 { slice.iter().sum() } println!("Sum of slice1: {}", sum_slice(slice1)); println!("Sum of v: {}", sum_slice(&v)); // Mutable slices let mut v = vec![1, 2, 3, 4, 5]; let slice = &mut v[2..4]; slice[0] = 30; slice[1] = 40; println!("Modified v: {:?}", v); // Split into slices let (left, right) = v.split_at_mut(3); left[0] = 10; right[0] = 50; println!("After split: {:?}", v); } Converting Between Vectors and Slices
fn main() { // Vector to slice let v = vec![1, 2, 3, 4, 5]; let slice: &[i32] = &v; // Slice to vector let arr = [1, 2, 3, 4, 5]; let v_from_slice: Vec<i32> = arr.to_vec(); let v_from_slice2 = Vec::from(&arr[1..4]); println!("v_from_slice: {:?}", v_from_slice); println!("v_from_slice2: {:?}", v_from_slice2); // as_slice and as_mut_slice let mut v = vec![1, 2, 3]; let s = v.as_slice(); println!("as_slice: {:?}", s); let s_mut = v.as_mut_slice(); s_mut[0] = 10; println!("After as_mut_slice: {:?}", v); // Converting between types let v: Vec<i32> = (0..5).collect(); let boxed_slice: Box<[i32]> = v.into_boxed_slice(); let v_back: Vec<i32> = boxed_slice.into_vec(); println!("Back to vector: {:?}", v_back); } 8. Common Vector Operations
Searching and Finding
fn main() { let v = vec![1, 2, 3, 4, 5, 3, 2, 1]; // Contains let has_three = v.contains(&3); println!("Contains 3: {}", has_three); // Find index of first occurrence if let Some(pos) = v.iter().position(|&x| x == 3) { println!("First 3 at position: {}", pos); } // Find index of last occurrence if let Some(pos) = v.iter().rposition(|&x| x == 3) { println!("Last 3 at position: {}", pos); } // Find element if let Some(&element) = v.iter().find(|&&x| x > 3) { println!("First element > 3: {}", element); } // Binary search (requires sorted vector) let mut sorted = v.clone(); sorted.sort(); match sorted.binary_search(&3) { Ok(pos) => println!("3 found at position {} in sorted", pos), Err(pos) => println!("3 not found, would be at position {}", pos), } // Check if all elements satisfy condition let all_positive = v.iter().all(|&x| x > 0); println!("All positive: {}", all_positive); // Check if any element satisfies condition let any_even = v.iter().any(|&x| x % 2 == 0); println!("Any even: {}", any_even); } Sorting and Ordering
fn main() { let mut numbers = vec![5, 2, 8, 1, 9, 3, 7, 4, 6]; // Sort in ascending order numbers.sort(); println!("Ascending: {:?}", numbers); // Sort in descending order numbers.sort_by(|a, b| b.cmp(a)); println!("Descending: {:?}", numbers); // Sort with custom comparator let mut people = vec![ ("Alice", 30), ("Bob", 25), ("Charlie", 35), ("Alice", 20), ]; people.sort_by(|a, b| { a.0.cmp(&b.0).then(a.1.cmp(&b.1)) }); println!("Sorted people: {:?}", people); // Partial sort (only sort part of the vector) let mut v = vec![5, 2, 8, 1, 9, 3, 7, 4, 6]; v.sort_unstable(); // Faster but doesn't preserve order of equal elements println!("Unstable sort: {:?}", v); // Reverse let mut v = vec![1, 2, 3, 4, 5]; v.reverse(); println!("Reversed: {:?}", v); // Rotate v.rotate_left(2); println!("Rotated left by 2: {:?}", v); v.rotate_right(1); println!("Rotated right by 1: {:?}", v); } Combining Vectors
fn main() { // Concatenation let v1 = vec![1, 2, 3]; let v2 = vec![4, 5, 6]; // Method 1: extend let mut v3 = v1.clone(); v3.extend(v2.clone()); println!("Extended: {:?}", v3); // Method 2: chain let v4: Vec<_> = v1.iter().chain(v2.iter()).cloned().collect(); println!("Chained: {:?}", v4); // Method 3: append (moves) let mut v5 = v1.clone(); let mut v6 = v2.clone(); v5.append(&mut v6); println!("Appended: {:?}, v6 empty: {:?}", v5, v6); // Join (for vectors of strings) let strings = vec!["hello", "world", "!"]; let joined = strings.join(" "); println!("Joined: '{}'", joined); // Split let numbers = vec![1, 2, 3, 4, 5, 6]; let (first, second): (Vec<_>, Vec<_>) = numbers.iter() .partition(|&&x| x < 4); println!("First partition: {:?}, Second: {:?}", first, second); // Zip let names = vec!["Alice", "Bob", "Charlie"]; let ages = vec![30, 25, 35]; let zipped: Vec<_> = names.iter().zip(ages.iter()).collect(); println!("Zipped: {:?}", zipped); } 9. Advanced Vector Techniques
Vector as Stack
fn main() { let mut stack: Vec<i32> = Vec::new(); // Push onto stack stack.push(1); stack.push(2); stack.push(3); println!("Stack: {:?}", stack); // Pop from stack while let Some(top) = stack.pop() { println!("Popped: {}", top); } // Using as stack with capacity let mut stack = Vec::with_capacity(5); for i in 0..5 { stack.push(i); println!("Push {}, len: {}, cap: {}", i, stack.len(), stack.capacity()); } // Peek at top without popping if let Some(top) = stack.last() { println!("Top: {}", top); } // Stack operations fn evaluate_postfix(expression: &str) -> Option<i32> { let mut stack = Vec::new(); for token in expression.split_whitespace() { match token.parse::<i32>() { Ok(num) => stack.push(num), Err(_) => { let b = stack.pop()?; let a = stack.pop()?; match token { "+" => stack.push(a + b), "-" => stack.push(a - b), "*" => stack.push(a * b), "/" => stack.push(a / b), _ => return None, } } } } stack.pop() } let result = evaluate_postfix("3 4 + 2 *"); println!("Postfix result: {:?}", result); // (3+4)*2 = 14 } Vector as Queue
use std::collections::VecDeque; fn main() { // VecDeque is better for queue operations let mut queue = VecDeque::new(); // Add to back queue.push_back(1); queue.push_back(2); queue.push_back(3); println!("Queue: {:?}", queue); // Add to front queue.push_front(0); println!("After push_front: {:?}", queue); // Remove from front while let Some(front) = queue.pop_front() { println!("Processing: {}", front); } // Using Vec as queue (less efficient) let mut queue_vec = Vec::new(); queue_vec.push(1); queue_vec.push(2); queue_vec.push(3); while !queue_vec.is_empty() { let front = queue_vec.remove(0); // O(n) operation! println!("Processing (Vec): {}", front); } // Ring buffer with VecDeque let mut buffer = VecDeque::with_capacity(5); for i in 0..10 { if buffer.len() == 5 { buffer.pop_front(); } buffer.push_back(i); println!("Buffer: {:?}", buffer); } } Matrix Operations with Vectors
#[derive(Debug)] struct Matrix { data: Vec<Vec<i32>>, rows: usize, cols: usize, } impl Matrix { fn new(rows: usize, cols: usize) -> Self { Matrix { data: vec![vec![0; cols]; rows], rows, cols, } } fn from_vec(data: Vec<Vec<i32>>) -> Option<Self> { if data.is_empty() { return None; } let rows = data.len(); let cols = data[0].len(); // Check if all rows have same length if data.iter().any(|row| row.len() != cols) { return None; } Some(Matrix { data, rows, cols, }) } fn get(&self, row: usize, col: usize) -> Option<&i32> { if row < self.rows && col < self.cols { Some(&self.data[row][col]) } else { None } } fn set(&mut self, row: usize, col: usize, value: i32) -> Option<()> { if row < self.rows && col < self.cols { self.data[row][col] = value; Some(()) } else { None } } fn transpose(&self) -> Matrix { let mut result = Matrix::new(self.cols, self.rows); for i in 0..self.rows { for j in 0..self.cols { result.data[j][i] = self.data[i][j]; } } result } fn multiply(&self, other: &Matrix) -> Option<Matrix> { if self.cols != other.rows { return None; } let mut result = Matrix::new(self.rows, other.cols); for i in 0..self.rows { for j in 0..other.cols { let mut sum = 0; for k in 0..self.cols { sum += self.data[i][k] * other.data[k][j]; } result.data[i][j] = sum; } } Some(result) } } fn main() { let mut mat = Matrix::new(3, 3); // Fill matrix for i in 0..3 { for j in 0..3 { mat.set(i, j, (i * 3 + j + 1) as i32).unwrap(); } } println!("Original matrix: {:?}", mat); // Get element if let Some(val) = mat.get(1, 1) { println!("Element at (1,1): {}", val); } // Transpose let transposed = mat.transpose(); println!("Transposed: {:?}", transposed); // Matrix multiplication let mat2 = Matrix::new(3, 3); if let Some(product) = mat.multiply(&mat2) { println!("Product: {:?}", product); } } 10. Error Handling with Vectors
Safe Access Patterns
fn main() { let v = vec![1, 2, 3, 4, 5]; // Safe indexing with match match v.get(10) { Some(value) => println!("Value: {}", value), None => println!("Index out of bounds"), } // Safe indexing with if let if let Some(value) = v.get(2) { println!("Found: {}", value); } // Using unwrap_or for defaults let value = v.get(10).unwrap_or(&-1); println!("Default value: {}", value); // Custom error type #[derive(Debug)] enum VecError { IndexOutOfBounds(usize, usize), EmptyVector, } fn safe_first<T>(v: &[T]) -> Result<&T, VecError> { v.first().ok_or(VecError::EmptyVector) } fn safe_get<T>(v: &[T], index: usize) -> Result<&T, VecError> { v.get(index).ok_or(VecError::IndexOutOfBounds(index, v.len())) } match safe_get(&v, 10) { Ok(val) => println!("Got: {}", val), Err(VecError::IndexOutOfBounds(idx, len)) => { println!("Index {} out of bounds for length {}", idx, len); } Err(VecError::EmptyVector) => println!("Vector is empty"), } // Try operations fn try_operations(v: &[i32]) -> Option<i32> { let first = v.first()?; let last = v.last()?; Some(first + last) } if let Some(sum) = try_operations(&v) { println!("Sum of first and last: {}", sum); } } Fallible Vector Operations
use std::num::ParseIntError; fn main() { // Collecting Results let strings = vec!["1", "2", "abc", "3", "def"]; // Collect into Result<Vec<i32>, ParseIntError> let parsed: Result<Vec<i32>, ParseIntError> = strings .iter() .map(|s| s.parse::<i32>()) .collect(); match parsed { Ok(numbers) => println!("All parsed: {:?}", numbers), Err(e) => println!("Error parsing: {}", e), } // Filter out errors let successful: Vec<i32> = strings .iter() .filter_map(|s| s.parse().ok()) .collect(); println!("Successful: {:?}", successful); // Partition results let (successes, failures): (Vec<_>, Vec<_>) = strings .iter() .map(|s| s.parse::<i32>()) .partition(Result::is_ok); let successes: Vec<_> = successes.into_iter().map(Result::unwrap).collect(); let failures: Vec<_> = failures.into_iter().map(Result::unwrap_err).collect(); println!("Successes: {:?}", successes); println!("Failures: {:?}", failures); // Try fold for early termination let numbers = vec![1, 2, 3, 4, 5]; let result = numbers.iter().try_fold(0, |acc, &x| { if x == 3 { None } else { Some(acc + x) } }); match result { Some(sum) => println!("Sum until 3: {}", sum), None => println!("Stopped at 3"), } } 11. Parallel Processing with Vectors
Using Rayon for Parallel Iteration
// Add rayon = "1.7" to Cargo.toml use rayon::prelude::*; fn main() { let numbers: Vec<i32> = (0..1_000_000).collect(); // Parallel map let start = std::time::Instant::now(); let squares: Vec<i32> = numbers.par_iter() .map(|&x| x * x) .collect(); println!("Parallel map time: {:?}", start.elapsed()); // Parallel filter let evens: Vec<&i32> = numbers.par_iter() .filter(|&&x| x % 2 == 0) .collect(); println!("Found {} evens", evens.len()); // Parallel fold let sum: i32 = numbers.par_iter() .fold(|| 0, |acc, &x| acc + x) .sum(); println!("Parallel sum: {}", sum); // Parallel any/all let has_negative = numbers.par_iter().any(|&x| x < 0); let all_positive = numbers.par_iter().all(|&x| x >= 0); println!("Has negative: {}, All positive: {}", has_negative, all_positive); // Parallel find if let Some(first_even) = numbers.par_iter().find_first(|&&x| x % 2 == 0) { println!("First even: {}", first_even); } // Parallel sort (rayon doesn't have parallel sort, but we can use par_sort) let mut unsorted: Vec<i32> = (0..100_000).rev().collect(); unsorted.par_sort(); // Uses rayon's parallel sort println!("First few after sort: {:?}", &unsorted[..5]); } 12. Common Patterns and Best Practices
Builder Pattern with Vectors
#[derive(Debug, Clone)] struct QueryBuilder { select: Vec<String>, from: String, where_clauses: Vec<String>, order_by: Vec<(String, bool)>, // (column, ascending) limit: Option<usize>, } impl QueryBuilder { fn new(table: &str) -> Self { QueryBuilder { select: vec!["*".to_string()], from: table.to_string(), where_clauses: Vec::new(), order_by: Vec::new(), limit: None, } } fn select(mut self, columns: Vec<&str>) -> Self { self.select = columns.into_iter().map(String::from).collect(); self } fn and_where(mut self, condition: &str) -> Self { self.where_clauses.push(condition.to_string()); self } fn order_by(mut self, column: &str, ascending: bool) -> Self { self.order_by.push((column.to_string(), ascending)); self } fn limit(mut self, limit: usize) -> Self { self.limit = Some(limit); self } fn build(&self) -> String { let select_str = if self.select.len() == 1 && self.select[0] == "*" { "*".to_string() } else { self.select.join(", ") }; let mut query = format!("SELECT {} FROM {}", select_str, self.from); if !self.where_clauses.is_empty() { query.push_str(" WHERE "); query.push_str(&self.where_clauses.join(" AND ")); } if !self.order_by.is_empty() { let order_str: Vec<String> = self.order_by .iter() .map(|(col, asc)| format!("{} {}", col, if *asc { "ASC" } else { "DESC" })) .collect(); query.push_str(" ORDER BY "); query.push_str(&order_str.join(", ")); } if let Some(limit) = self.limit { query.push_str(&format!(" LIMIT {}", limit)); } query } } fn main() { let query = QueryBuilder::new("users") .select(vec!["id", "name", "email"]) .and_where("age > 18") .and_where("active = true") .order_by("name", true) .limit(10) .build(); println!("{}", query); } Pool Pattern with Vectors
struct ObjectPool<T> { objects: Vec<T>, max_size: usize, } impl<T> ObjectPool<T> { fn new(max_size: usize) -> Self { ObjectPool { objects: Vec::with_capacity(max_size), max_size, } } fn acquire(&mut self) -> Option<T> { self.objects.pop() } fn release(&mut self, obj: T) -> Result<(), T> { if self.objects.len() < self.max_size { self.objects.push(obj); Ok(()) } else { Err(obj) // Pool full } } fn with_capacity(capacity: usize, f: impl Fn() -> T) -> Self { let mut objects = Vec::with_capacity(capacity); for _ in 0..capacity { objects.push(f()); } ObjectPool { objects, max_size: capacity, } } } #[derive(Debug)] struct Connection { id: usize, connected: bool, } impl Connection { fn new(id: usize) -> Self { Connection { id, connected: true, } } fn query(&self, sql: &str) { println!("Connection {} executing: {}", self.id, sql); } } fn main() { let mut pool = ObjectPool::with_capacity(3, || Connection::new(rand::random())); // Acquire connections if let Some(mut conn) = pool.acquire() { conn.query("SELECT * FROM users"); // Release back to pool let _ = pool.release(conn); } // Pool full example for i in 0..5 { let conn = Connection::new(i); match pool.release(conn) { Ok(()) => println!("Connection {} added to pool", i), Err(conn) => println!("Pool full, connection {} dropped", conn.id), } } } 13. Testing and Documentation
Testing Vector Operations
#[cfg(test)] mod tests { #[test] fn test_vector_creation() { let v1 = vec![1, 2, 3]; assert_eq!(v1.len(), 3); assert_eq!(v1[0], 1); let v2: Vec<i32> = (0..5).collect(); assert_eq!(v2, vec![0, 1, 2, 3, 4]); } #[test] fn test_vector_operations() { let mut v = vec![1, 2, 3]; v.push(4); assert_eq!(v, vec![1, 2, 3, 4]); let last = v.pop(); assert_eq!(last, Some(4)); assert_eq!(v, vec![1, 2, 3]); v.insert(1, 10); assert_eq!(v, vec![1, 10, 2, 3]); } #[test] fn test_vector_iteration() { let v = vec![1, 2, 3]; let sum: i32 = v.iter().sum(); assert_eq!(sum, 6); let doubled: Vec<i32> = v.iter().map(|&x| x * 2).collect(); assert_eq!(doubled, vec![2, 4, 6]); } #[test] #[should_panic(expected = "index out of bounds")] fn test_out_of_bounds() { let v = vec![1, 2, 3]; let _ = v[5]; // This panics } #[test] fn test_safe_access() { let v = vec![1, 2, 3]; assert_eq!(v.get(5), None); assert_eq!(v.get(1), Some(&2)); } } Documentation Examples
/// A custom vector wrapper with additional functionality /// /// # Examples /// /// ``` /// use mylib::MyVec; /// /// let mut v = MyVec::new(); /// v.push(1); /// v.push(2); /// v.push(3); /// /// assert_eq!(v.sum(), 6); /// assert_eq!(v.average(), 2.0); /// ``` #[derive(Debug, Clone)] pub struct MyVec<T> { data: Vec<T>, } impl<T> MyVec<T> { /// Creates a new empty `MyVec` pub fn new() -> Self { MyVec { data: Vec::new() } } /// Adds an element to the vector /// /// # Examples /// /// ``` /// let mut v = MyVec::new(); /// v.push(42); /// assert_eq!(v.len(), 1); /// ``` pub fn push(&mut self, value: T) { self.data.push(value); } /// Returns the number of elements pub fn len(&self) -> usize { self.data.len() } /// Returns true if the vector is empty pub fn is_empty(&self) -> bool { self.data.is_empty() } } impl MyVec<i32> { /// Returns the sum of all elements /// /// # Panics /// /// Panics if the vector is empty pub fn sum(&self) -> i32 { self.data.iter().sum() } /// Returns the average of all elements /// /// # Panics /// /// Panics if the vector is empty pub fn average(&self) -> f64 { self.sum() as f64 / self.len() as f64 } } impl<T> Default for MyVec<T> { fn default() -> Self { Self::new() } } Conclusion
Vectors are one of the most versatile and commonly used collections in Rust:
Key Takeaways
- Dynamic sizing: Vectors grow and shrink at runtime
- Memory efficient: Elements stored contiguously
- Fast access: O(1) index access
- Ownership: Vector owns its elements
- Rich API: Extensive set of methods for manipulation
- Performance: Control over capacity for optimization
Common Operations Summary
| Operation | Method | Time Complexity |
|---|---|---|
| Create empty | Vec::new() | O(1) |
| Create with values | vec![1, 2, 3] | O(n) |
| Add to end | push() | O(1) amortized |
| Remove from end | pop() | O(1) |
| Insert at index | insert() | O(n) |
| Remove at index | remove() | O(n) |
| Get element | get() | O(1) |
| Iterate | iter() | O(n) |
| Find element | position() | O(n) |
| Sort | sort() | O(n log n) |
Best Practices
- Pre-allocate capacity when you know the size in advance
- Use
get()for safe indexing instead of direct indexing - Prefer iterators over manual indexing when possible
- Use
Vec::with_capacity()for performance-critical code - Consider
VecDequeif you need to add/remove from both ends - Use
shrink_to_fit()to free unused memory - Be mindful of ownership when passing vectors to functions
Vectors are fundamental to Rust programming and mastering them is essential for writing efficient and idiomatic Rust code.