Vec::try_reserve¶
Level: reference · for working programmers
One line: reserve that returns Err instead of aborting the process.
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::reserve— the aborting version, and the defaultVec::try_reserve_exact— the same, without slackVec::capacity— checking what you actually got