How to find an element in a Vec
Short answer: iter().find(predicate) returns Option<&T> — the first element that
matches, borrowed from the Vec. When what you wanted was the index, use
iter().position(predicate), which returns Option<usize>.
From a fresh project — cargo new find-demo — replace the contents of 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.
find returns the element
Section titled “find returns the element”fn main() { let languages = vec!["Rust", "Go", "Python"];
let found = languages.iter().find(|&&l| l == "Go"); println!("{found:?}");
let missing = languages.iter().find(|&&l| l == "Java"); println!("{missing:?}");}Some("Go")Nonefind walks from the front and stops at the first match, so it hands back an Option:
the search can fail. The reference inside the Some borrows the Vec, which is why the
answer is &"Go" and not "Go" — the same reason get returns Option<&T>.
Why the predicate gets an extra &
Section titled “Why the predicate gets an extra &”vec.iter() over a Vec<&str> yields &&str, and find’s closure takes one more
reference on top of that, so its parameter is &&&str. The comparison written the
obvious way does not compile:
fn main() { let languages = vec!["Rust", "Go", "Python"]; let found = languages.iter().find(|l| l == "Go"); println!("{found:?}");}error[E0277]: can't compare `&&str` with `str` --> src/main.rs:3:45 |3 | let found = languages.iter().find(|l| l == "Go"); | ^^ no implementation for `&&str == str` | = help: the trait `PartialEq<str>` is not implemented for `&&str` = note: required for `&&&str` to implement `PartialEq<&str>`help: consider dereferencing here |3 | let found = languages.iter().find(|l| **l == "Go"); | ++
For more information about this error, try `rustc --explain E0277`.The compiler names both sides of the comparison and its own fix. Peel the layers with
* or with a pattern: |l| **l == "Go" and |&&l| l == "Go" are the same predicate.
Field access does not have this problem — it dereferences for you.
position returns the index
Section titled “position returns the index”fn main() { let mut languages = vec!["Rust", "Go", "Python"];
let index = languages.iter().position(|&l| l == "Go"); println!("{index:?}");
if let Some(i) = index { languages[i] = "Golang"; } println!("{languages:?}");}Some(1)["Rust", "Golang", "Python"]This is the call most people actually want. An index feeds languages[i], remove,
and anything else that counts. position’s closure takes the item itself (&str here,
one reference fewer than find’s) and returns a plain usize, which borrows
nothing.
That difference matters the moment you want to change the Vec. find’s answer borrows
it for as long as the reference lives, so this fails:
fn main() { let mut languages = vec!["Rust", "Go", "Python"]; let found = languages.iter().find(|&&l| l == "Go"); languages.push("Java"); println!("{found:?}");}error[E0502]: cannot borrow `languages` as mutable because it is also borrowed as immutable --> src/main.rs:4:5 |3 | let found = languages.iter().find(|&&l| l == "Go"); | --------- immutable borrow occurs here4 | languages.push("Java"); | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here5 | println!("{found:?}"); | ----- immutable borrow later used here
For more information about this error, try `rustc --explain E0502`.position sidesteps it: the index is Copy, the borrow ends when the call ends, and
the mutation is a separate statement. To change the element in place without going
through an index, iter_mut().find(|n| **n == 20) hands back Option<&mut T>.
First match or last
Section titled “First match or last”find and position stop at the first match; rposition starts at the back. On a Vec
with repeats:
fn main() { let nums = vec![1, 2, 1, 3, 1]; let first = nums.iter().position(|&n| n == 1); let last = nums.iter().rposition(|&n| n == 1); println!("{first:?} {last:?}");}Some(0) Some(4)rposition needs a double-ended, exact-size iterator, and slice::Iter is both. The
index it returns still counts from the front, so the two answers are used the same way.
Finding by a field
Section titled “Finding by a field”The predicate can look at anything reachable from the element, which is usually how structs are searched:
#[derive(Debug)]struct Person { name: String, age: u32,}
fn main() { let people = vec![ Person { name: "Ada".to_string(), age: 36 }, Person { name: "Grace".to_string(), age: 45 }, ];
let found = people.iter().find(|p| p.age == 45); println!("{found:?}");
let age = people.iter().find(|p| p.name == "Grace").map(|p| p.age); println!("{age:?}");}Some(Person { name: "Grace", age: 45 })Some(45)p.age needs no * because field access dereferences through both references. The
second line shows the other half of the pattern: find borrows, so copy the field out
with map when the answer has to outlive the borrow.
If you only need a yes-or-no answer, contains is the direct call. On a Vec that is
already sorted, binary_search does the same job in logarithmic time; it returns a
Result, Ok with the index when the value is there and Err with the insertion
point when it is not.
Related
Section titled “Related”- How to get an element from a Vec without panicking —
get, the other lookup that returns anOption - How to sort a Vec — the
sortthatbinary_searchwants first - How to loop over a Vec without moving it — what
iter()borrows
Next in this chapter: how to split a Vec into chunks.