Skip to content
Select theme

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.

languages[0] = Rust
languages.get(1) = Some("Go")
languages.get(5) = None

Same 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 5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

The 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.

second: Go
index 5 -> unknown
len at index 5 -> 0

match 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.

Some(["Go", "Python"])
Some(["Go", "Python"])
None
Some("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.

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.

Next in this chapter: why you can’t modify a Vec while iterating over it.