How to get an element from a Vec without panicking
Short answer: vec.get(i) returns Option<&T> — Some(&value) when the index is inside
the Vec, None when it is past the end. Indexing with vec[i] panics instead.
From a fresh project — cargo new vec-get-demo, then replace src/main.rs with each block
below and run it with cargo run. The project name shows up only in cargo’s own output;
nothing else here depends on it.
Two ways to read by position
Section titled “Two ways to read by position”fn main() { let languages = vec!["Rust", "Go", "Python"];
let first = languages[0]; println!("languages[0] = {first}");
let second = languages.get(1); println!("languages.get(1) = {second:?}");
let missing = languages.get(5); println!("languages.get(5) = {missing:?}");}languages[0] = Rustlanguages.get(1) = Some("Go")languages.get(5) = NoneSame read, different answer type. languages[0] hands back the element itself; get(1)
hands back Some(&"Go") and get(5) hands back None. The Option is the
point: the compiler won’t let you touch the value until you have said what happens when
it is missing.
get hands back a reference, so the answer borrows the Vec. Copying a large element would
cost more than the borrow, and the borrow is why the Some holds &"Go" rather than
"Go".
What indexing does when the index is wrong
Section titled “What indexing does when the index is wrong”fn main() { let languages = vec!["Rust", "Go", "Python"]; let missing = languages[5]; println!("{missing}");}thread 'main' panicked at src/main.rs:3:28:index out of bounds: the len is 3 but the index is 5note: run with `RUST_BACKTRACE=1` environment variable to display a backtraceThe process dies with exit code 101. [] isn’t wrong — it is the right call when your own
logic guarantees the index: a loop counter bounded by len(), a position checked one line
earlier. get is for when the index arrives from outside — user input, a file, a lookup
that can miss. The compiler can’t tell those situations apart, so [] checks at run time
and get hands the decision to you.
Reading the Option
Section titled “Reading the Option”fn main() { let languages = vec!["Rust", "Go", "Python"];
match languages.get(1) { Some(language) => println!("second: {language}"), None => println!("no second element"), }
let fallback = languages.get(5).copied().unwrap_or("unknown"); println!("index 5 -> {fallback}");
let heavy = languages.get(5).map(|l| l.len()).unwrap_or_else(|| 0); println!("len at index 5 -> {heavy}");}second: Goindex 5 -> unknownlen at index 5 -> 0match forces both arms, which is the habit worth building. unwrap_or takes a default
that is cheap to build; unwrap_or_else takes a closure, so the default is only computed
when it is needed. One trap: .unwrap() on that None panics with
called `Option::unwrap()` on a `None` value — the same crash, one step later. If you
were reaching for unwrap, the index wasn’t guaranteed after all.
Ranges, and the two ends
Section titled “Ranges, and the two ends”fn main() { let languages = vec!["Rust", "Go", "Python"];
println!("{:?}", languages.get(1..3)); println!("{:?}", languages.get(1..=2)); println!("{:?}", languages.get(2..5));
println!("{:?} {:?}", languages.first(), languages.last()); println!("{:?}", Vec::<i32>::new().first());
let mut numbers = vec![10, 20, 30]; if let Some(first_number) = numbers.get_mut(0) { *first_number += 5; } println!("{numbers:?}");}Some(["Go", "Python"])Some(["Go", "Python"])NoneSome("Rust") Some("Python")None[15, 20, 30]get takes a range as happily as a number, and gives back Option<&[T]> — a slice of the
Vec, or None when the range runs past the end. &languages[2..5] panics in exactly that
case, with range end index 5 out of range for slice of length 3. first() and last()
are get(0) and get(len() - 1) with the arithmetic done for you; on an empty Vec both
are None. get_mut is the same call with Option<&mut T> back.
No negative indices
Section titled “No negative indices”fn main() { let languages = vec!["Rust", "Go", "Python"]; let last = languages.get(-1); println!("{last:?}");}error[E0277]: the trait bound `usize: Neg` is not satisfied --> src/main.rs:3:30 |3 | let last = languages.get(-1); | ^^ the trait `Neg` is not implemented for `usize` |help: consider specifying an integer type that can be negative |3 | let last = languages.get(-1isize); | +++++
For more information about this error, try `rustc --explain E0277`.Indices are usize, so there is no vec[-1] — the compiler refuses before the program
runs. Taking its suggestion does not help either: get(-1isize) fails with
slice indices are of type `usize` or ranges of `usize` . Counting from the end goes
through len(), or through last(), which is the same answer without the subtraction.
Related
Section titled “Related”Next in this chapter: why you can’t modify a Vec while iterating over it.