Complete Guide to Rust Vectors

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

  1. Dynamic sizing: Vectors grow and shrink at runtime
  2. Memory efficient: Elements stored contiguously
  3. Fast access: O(1) index access
  4. Ownership: Vector owns its elements
  5. Rich API: Extensive set of methods for manipulation
  6. Performance: Control over capacity for optimization

Common Operations Summary

OperationMethodTime Complexity
Create emptyVec::new()O(1)
Create with valuesvec![1, 2, 3]O(n)
Add to endpush()O(1) amortized
Remove from endpop()O(1)
Insert at indexinsert()O(n)
Remove at indexremove()O(n)
Get elementget()O(1)
Iterateiter()O(n)
Find elementposition()O(n)
Sortsort()O(n log n)

Best Practices

  1. Pre-allocate capacity when you know the size in advance
  2. Use get() for safe indexing instead of direct indexing
  3. Prefer iterators over manual indexing when possible
  4. Use Vec::with_capacity() for performance-critical code
  5. Consider VecDeque if you need to add/remove from both ends
  6. Use shrink_to_fit() to free unused memory
  7. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper