Iterators: laziness and the & in every closure
Published 17 October 2026
Adapters are lazy¶
let parsed = ["x", "42", "7"].iter().filter_map(|s| s.parse::<i32>().ok());
println!("{:?}", parsed);
// FilterMap { iter: Iter(["x", "42", "7"]) }
filter_map, map, filter and friends return an iterator adapter
(FilterMap<slice::Iter<&str>, {closure}>), not a collection. Nothing runs until
something consumes it. FilterMap’s Debug prints only the wrapped iterator, which is
why the original strings show up.
Consume it with collect (or sum, find, a for loop, …):
let parsed: Vec<i32> = ["x", "42", "7"]
.iter()
.filter_map(|s| s.parse::<i32>().ok())
.collect();
println!("{:?}", parsed); // [42, 7]
To fail on any bad input instead of silently dropping it, collect into a Result:
let all: Result<Vec<i32>, _> = ["x", "42", "7"].iter().map(|s| s.parse::<i32>()).collect();
assert!(all.is_err()); // because of "x"
.iter() yields references¶
On [1, 2, 3, 4], .iter() yields &i32. So why does this compile?
n is &i32, and &i32 * i32 is defined. The standard library implements every
arithmetic operator for references to primitives:
impl Mul<i32> for i32 // i32 * i32
impl Mul<i32> for &i32 // &i32 * i32 <- this case
impl Mul<&i32> for i32 // i32 * &i32
impl Mul<&i32> for &i32 // &i32 * &i32
// all four: Output = i32
This is not auto-deref (operators never auto-deref). It’s explicit impls, and only
primitives get them. Your own Copy type needs *m * 2.
Comparison operators don’t have these impls, so the reference bites there:
[1, 2, 3].iter().filter(|n| n > 1); // ❌ n: &&i32, can't compare with an integer
[1, 2, 3].iter().filter(|n| **n > 1); // ✅
[1, 2, 3].iter().filter(|&&n| n > 1); // ✅ destructure in the pattern
[1, 2, 3].iter().map(|n| n.pow(2)); // ✅ method calls do auto-deref
To skip the reference entirely: .into_iter() on an array (edition 2021+) or
.iter().copied(), and items are plain i32.
filter and find add another &¶
Their predicates take &Self::Item, because they need to keep the item to yield or
return it.
So count the layers:
let result = [1, 2, 3, 4].iter().map(|n| n * 2).find(|n| *n > 5);
println!("{:?}", result); // Some(6)
| Step | Type |
|---|---|
.iter() yields |
&i32 |
.map(\|n\| n * 2) yields |
i32 |
find’s predicate gets |
&i32 |
find returns |
Option<i32> |
*n is right; **n fails with error[E0614]: type `i32` cannot be dereferenced. The
map removed a layer. Without it you do get &&i32:
Equivalent predicates for the map case:
.find(|&n| n > 5) // n: i32
.find(|n| *n > 5)
.find(|n| n > &5) // &i32 vs &i32
// .find(|n| n > 5) // ❌ no PartialOrd<i32> for &i32
Drill: count the reference levels¶
Keep this rule in mind:
filtergets&Item, because it only checks whether to keep the item. It has to hand the item back afterwards, so it can only lend it to the predicate..iter()makesItem = &i32, sofiltergets&&i32.
So the type in a filter closure is always one more & than the item type. Work
out the item type first, then add one:
| Chain | Item |
filter gets |
A predicate that works |
|---|---|---|---|
[1, 2, 3].iter().filter(...) |
&i32 |
&&i32 |
\|n\| **n > 1 |
[1, 2, 3].into_iter().filter(...) |
i32 |
&i32 |
\|n\| *n > 1 |
[1, 2, 3].iter().copied().filter(...) |
i32 |
&i32 |
\|n\| *n > 1 |
[1, 2, 3].iter().map(\|n\| n * 2).filter(...) |
i32 |
&i32 |
\|n\| *n > 2 |
["a", "bb"].iter().filter(...) |
&&str |
&&&str |
\|s\| s.len() > 1 |
The last row looks scary, but method calls auto-deref through any number of &, so
s.len() just works. Only operators make you count.
filter doesn’t change the item type, so whatever comes after it gets the item back
with the extra & gone:
let big: Vec<i32> = [1, 2, 3]
.iter()
.filter(|n| **n > 1) // n: &&i32
.map(|n| n * 10) // n: &i32 again
.collect(); // [20, 30]
When you’d rather not count, put the &s in the pattern instead: |&&n| n > 1 on
.iter(), |&n| n > 1 after .copied() or .into_iter(). Each & in the pattern
peels off one layer.
map, find_map and friends¶
map transforms every item. find_map transforms each item into an Option and stops
at the first Some. It’s filter_map(...).next(), but it says “first successful
result” directly.
| Method | Closure gets | Closure returns | Result |
|---|---|---|---|
map |
Item |
U |
lazy iterator of U |
filter_map |
Item |
Option<U> |
lazy iterator of every Some |
find_map |
Item |
Option<U> |
Option<U>: the first Some, then stops |
flat_map |
Item |
an iterator | lazy iterator, flattened |
find |
&Item |
bool |
Option<Item>: the first match |
position |
Item |
bool |
Option<usize>: index of the first match |
any |
Item |
bool |
bool |
Note the & column: find (like filter) gets &Item because it keeps the item to
return it. position and any only return an index or a bool, so they take the item
itself. On .iter(), that’s **n in find but *n in position.
And the three ways in: iter() yields &T, iter_mut() yields &mut T, into_iter()
yields T.
Quiz¶
let a: Vec<_> = ["x", "42", "7"].iter().filter_map(|s| s.parse::<i32>().ok()).collect();
// [42, 7]: skips "x", keeps every success
let b: Option<i32> = ["x", "42", "7"].iter().find_map(|s| s.parse::<i32>().ok());
// Some(42): stops at the first success; None if nothing parses
let c = [1, 2, 3, 4].iter().find(|n| **n > 2);
// Some(&3): find returns the item, and .iter() items are references
let d = [1, 2, 3, 4].iter().position(|n| *n > 2);
// Some(2): Option<usize>, the zero-based index of 3, not the value
let e: Vec<_> = [1, 2, 3, 4].iter().flat_map(|n| 0..*n).collect();
// [0, 0, 1, 0, 1, 2, 0, 1, 2, 3]: 0..1, 0..2, 0..3, 0..4 joined
The trap: c and d both print Some(...), but one is a reference to a value and the
other is an index.
Asking the compiler for a type¶
Assign to the wrong type and read the error (or hover in rust-analyzer):
let x: () = [1, 2, 3, 4].iter().map(|n| n * 2);
// expected `()`, found `Map<Iter<'_, {integer}>, {closure}>`
map returns a lazy Map whose Item = i32; collect() turns it into Vec<i32>.
See also &[&str] vs [&str] for
.iter() on a slice of &str.