Storing Lists of Values with Vectors

Vec<T> is a growable array type that stores values contiguously in memory. All elements must be of the same type.

Creating Vectors

// Empty vector with type annotation
let v: Vec<i32> = Vec::new();

// Using the vec! macro with initial values
let v = vec![1, 2, 3];

Updating Vectors

let mut v = Vec::new();
v.push(5);
v.push(6);
v.push(7);

The mut keyword is required for modification. Rust infers the type from the data.

Reading Elements

Two approaches for accessing elements:

let v = vec![1, 2, 3, 4, 5];

// Index syntax - panics on invalid index
let third: &i32 = &v[2];

// get method - returns Option<&T>
let third: Option<&i32> = v.get(2);

Use indexing when you want the program to crash on invalid access, use get() when you want to handle the error gracefully.

Borrowing Rules

let mut v = vec![1, 2, 3, 4, 5];
let first = &v[0];
v.push(6); // Error: cannot borrow as mutable
println!("The first element is: {first}");

Adding elements may require reallocation, invalidating existing references. The borrow checker prevents use-after-free errors.

Iteration

let v = vec![100, 32, 57];
for i in &v {
    println!("{i}");
}

// Mutable iteration
let mut v = vec![100, 32, 57];
for i in &mut v {
    *i += 50;
}

Storing Multiple Types with Enums

Vectors can only store one type, but enums allow storing variants of different types:

enum SpreadsheetCell {
    Int(i32),
    Float(f64),
    Text(String),
}

let row = vec![
    SpreadsheetCell::Int(3),
    SpreadsheetCell::Text(String::from("blue")),
    SpreadsheetCell::Float(10.12),
];

For truly dynamic types unknown at compile time, use trait objects (covered in Chapter 18).

Memory Management

Vectors are automatically freed when they go out of scope, along with their contents. The borrow checker ensures references remain valid.

{
    let v = vec![1, 2, 3, 4];
    // v is valid here
} // v goes out of scope and is freed