Skip to content
Select theme

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.

Some("Go")
None

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

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:

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.

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:

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 here
4 | languages.push("Java");
| ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
5 | 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>.

find and position stop at the first match; rposition starts at the back. On a Vec with repeats:

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.

The predicate can look at anything reachable from the element, which is usually how structs are searched:

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.

Next in this chapter: how to split a Vec into chunks.