Skip to content

Vec::try_reserve

Vec methods · Collections

Level: reference · for working programmers

One line: reserve that returns Err instead of aborting the process.

pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError>

Stable since 1.57.0.

Rust's allocation-failure path is normally an abort, not a panic — you cannot catch it and no destructor runs. try_reserve is the way out for code that must survive a failed allocation: a server sizing a buffer from a client-supplied length, a parser reading a declared size out of a file, anything where the number is not yours.

On failure the vector is untouched — same length, same capacity, same contents.

The error type is opaque on stable. It implements Display and Debug; TryReserveError::kind() is still unstable, so match on the fact of failure rather than on its variety.

The point is composition: out.try_reserve(n)? inside a function returning Result turns an abort into an ordinary error path.

None of this defends against overcommit. On a Linux box with default settings a large allocation usually succeeds and the process dies later when it touches the pages. try_reserve reports what the allocator said, which is not always what the kernel will honour.

Example

vec_try_reserve.rs in full — pasted here by tools/run_examples.py from the file CI compiles and runs.

use std::collections::TryReserveError;

fn main() {
    // The fallible reserve: a Result instead of an abort.
    let mut v: Vec<u32> = Vec::new();
    match v.try_reserve(10) {
        Ok(()) => println!("reserved, capacity now at least 10: {}", v.capacity() >= 10),
        Err(e) => println!("could not reserve: {e:?}"),
    }

    // A request that cannot be satisfied comes back as an Err rather than
    // killing the process. reserve() would abort here.
    let mut v: Vec<u64> = Vec::new();
    let huge = usize::MAX / 4;
    match v.try_reserve(huge) {
        Ok(()) => println!("somehow reserved usize::MAX/4 u64s"),
        Err(_) => println!("a usize::MAX/4 request returned Err — the process is still alive"),
    }
    println!("vector is untouched: len {} cap {}", v.len(), v.capacity());

    // Which is the whole point: it composes with ? in a fallible function.
    fn collect_bytes(src: &[u8]) -> Result<Vec<u8>, TryReserveError> {
        let mut out = Vec::new();
        out.try_reserve(src.len())?;
        out.extend_from_slice(src);
        Ok(out)
    }
    println!("{:?}", collect_bytes(&[1, 2, 3]));

    // The error type is opaque on stable — it prints, and that is all it
    // promises. (`TryReserveError::kind()` is still unstable.)
    let mut v: Vec<u8> = Vec::new();
    if let Err(e) = v.try_reserve(usize::MAX) {
        println!("error: {e}");
    }

    // Same "additional on top of len" arithmetic as reserve.
    let mut v = vec![1u8, 2, 3];
    v.try_reserve(5).unwrap();
    println!("len {} capacity at least 8: {}", v.len(), v.capacity() >= 8);
}

Verified output of vec_try_reserve.rs — regenerated by tools/run_examples.py, never hand-typed.

reserved, capacity now at least 10: true
a usize::MAX/4 request returned Err — the process is still alive
vector is untouched: len 0 cap 0
Ok([1, 2, 3])
error: memory allocation failed because the computed capacity exceeded the collection's maximum
len 3 capacity at least 8: true

See also

Vec::try_reserve in the standard library ↗