Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A Vec<T> (vector) is a Sized, contiguous, and dynamically growable array type that manages a heap-allocated buffer. It provides O(1)O(1) indexing, amortized O(1)O(1) push operations, and guarantees that its elements are stored sequentially in memory, making it highly cache-friendly.

Memory Layout

Under the hood, a Vec<T> is represented as a struct containing exactly three machine words:
  1. Pointer: A non-null pointer (internally represented as Unique<T>, which wraps NonNull<T>) to the start of the allocated heap buffer. This strict non-null guarantee enables Rust’s null-pointer optimization, allowing types like Option<Vec<T>> to have the exact same memory footprint as Vec<T>.
  2. Capacity: A usize representing the total number of elements the buffer can currently hold without triggering a reallocation.
  3. Length: A usize representing the number of initialized elements currently in the vector. The invariant len <= capacity is always maintained.

Initialization Syntax

Vectors can be initialized empty, pre-allocated, or populated via macros.
// 1. Empty vector. Does not allocate heap memory until the first element is pushed.
let mut v1: Vec<i32> = Vec::new();

// 2. Pre-allocated vector. Allocates heap space for at least 10 elements.
// The underlying allocator may round up the allocation size, so capacity can be >= 10.
// Prevents reallocation overhead if the maximum size is known upfront.
let mut v2: Vec<i32> = Vec::with_capacity(10);

// 3. Macro initialization with a list of elements.
let v3 = vec![1, 2, 3];

// 4. Macro initialization with a repeated value (requires T to implement Clone).
let v4 = vec![0; 5]; // Equivalent to [0, 0, 0, 0, 0]

Growth and Reallocation Mechanics

When an element is added to a Vec<T> and len == capacity, the vector must grow. The standard library handles this by:
  1. Allocating a new, larger heap buffer (historically doubling the capacity).
  2. Moving the existing elements from the old buffer to the new buffer.
  3. Deallocating the old buffer.
Because reallocation is an O(n)O(n) operation, pushing to a vector is an amortized O(1)O(1) operation. Using Vec::with_capacity bypasses this overhead by satisfying the capacity requirement at instantiation.

Ownership and Memory Management

Vec<T> owns its elements. When a vector goes out of scope, its Drop implementation is triggered. This sequentially calls the Drop trait on all initialized elements (from index 0 to len - 1) and subsequently frees the underlying heap buffer.
{
    let v = vec![String::from("A"), String::from("B")];
    // v owns the heap-allocated Strings.
} // v is dropped. The Strings are dropped, then the Vec's buffer is deallocated.

Slicing and Deref Coercion

Vec<T> implements both Deref<Target = [T]> and DerefMut. This means a vector automatically coerces to an immutable slice (&[T]) via Deref, or a mutable slice (&mut [T]) via DerefMut, when passed by reference. Slices provide a view into the vector’s contiguous memory without taking ownership.
let mut v = vec![10, 20, 30, 40, 50];

// Deref coercion allows calling slice methods directly on the Vec
let contains_thirty = v.contains(&30);

// Borrowing a specific range as an immutable slice (&[i32]) via Index
let slice: &[i32] = &v[1..4]; // [20, 30, 40]

// Borrowing as a mutable slice (&mut [i32]) via DerefMut coercion
let mut_slice: &mut [i32] = &mut v;
mut_slice[0] = 99;

Access Patterns

Accessing elements requires navigating Rust’s borrowing rules and bounds checking.
let v = vec![10, 20, 30];

// 1. Direct indexing: v[1] is a place expression evaluating to the element (type T).
// Taking a reference (&v[1]) yields &T. Panics if index >= len.
let val_ref: &i32 = &v[1]; 

// 2. Safe access: Returns Option<&T>. Returns None if index >= len.
let safe_val: Option<&i32> = v.get(5);

// 3. Mutable access: Returns Option<&mut T>.
let mut v_mut = vec![10, 20, 30];
if let Some(val) = v_mut.get_mut(0) {
    *val += 1; // v_mut is now [11, 20, 30]
}

Iteration Mechanics

Vectors support three primary modes of iteration, directly mapping to Rust’s ownership semantics:
let mut v = vec![1, 2, 3];

// 1. Immutable Borrow: Yields &T. Vector remains accessible.
for val in &v { /* ... */ }

// 2. Mutable Borrow: Yields &mut T. Vector remains accessible.
for val in &mut v { /* ... */ }

// 3. Consuming (IntoIter): Yields T. Vector is moved and destroyed.
for val in v { /* ... */ } 
// v is no longer accessible here.
Master Rust with Deep Grasping Methodology!Learn More