Vec<T> in Depth

reserve() and shrink_to_fit()

Vec<T> provides explicit capacity control methods—.reserve(), .reserve_exact(), .try_reserve(), .shrink_to(), .shrink_to_fit(), and .into_boxed_slice()—to pre-allocate space before bulk insertions or release unused heap memory after a vector reaches its final size.

Capacity Growth and Shrinking Reference

Method Parameter Meaning Allocation Strategy
v.reserve(additional) Space for additional more elements (target >= v.len() + additional) Geometric growth (at least doubles capacity to amortize future calls)
v.reserve_exact(additional) Space for additional more elements (target >= v.len() + additional) Allocates the exact minimum for v.len() + additional
v.try_reserve(additional) Same as reserve, but fallible Returns Result<(), TryReserveError> instead of panicking/aborting on OOM
v.shrink_to(min_capacity) Target total capacity Shrinks capacity down toward max(v.len(), min_capacity)
v.shrink_to_fit() None Shrinks capacity as close to v.len() as the allocator supports
v.into_boxed_slice() Consumes v Shrinks excess capacity and returns a 16-byte Box<[T]>

Common pitfall: The argument to v.reserve(additional) is the number of additional elements beyond v.len(), not the new total capacity! If v.len() == 10 and v.capacity() == 16, calling v.reserve(10) ensures total capacity is at least $10 + 10 = 20$.

Comparing reserve(), reserve_exact(), and shrink_to_fit()

fn main() {
    let mut v = vec![1, 2, 3]; // len = 3, cap = 3

    // 1. `reserve_exact(7)` requests exact capacity for 3 + 7 = 10 elements:
    v.reserve_exact(7);
    println!("After reserve_exact(7): len={}, cap={}", v.len(), v.capacity());

    // 2. `reserve(50)` reserves space for at least 3 + 50 = 53 elements:
    v.reserve(50);
    println!("After reserve(50):      len={}, cap={}", v.len(), v.capacity());

    // 3. `shrink_to(8)` lowers capacity to 8 while keeping all 3 elements:
    v.shrink_to(8);
    println!("After shrink_to(8):     len={}, cap={}", v.len(), v.capacity());

    // 4. `shrink_to_fit()` drops all remaining excess capacity:
    v.shrink_to_fit();
    println!("After shrink_to_fit():  len={}, cap={}", v.len(), v.capacity());
}
After reserve_exact(7): len=3, cap=10
After reserve(50):      len=3, cap=53
After shrink_to(8):     len=3, cap=8
After shrink_to_fit():  len=3, cap=3

Freezing a Growable Vec<T> into Box<[T]>

When a Vec<T> is finished growing and will remain fixed-length for the rest of its lifetime, calling v.into_boxed_slice() calls shrink_to_fit() and converts the 24-byte Vec<T> (ptr, len, capacity) into a 16-byte Box<[T]> (ptr, len), eliminating both unused heap capacity and the 8-byte capacity field on the stack.