Rust Iterator Adapters
An iterator adapter is a step that changes an iterator. Each adapter takes an iterator and hands back a new one.
map changes every item
map runs a function on each item and hands on the results:
let doubled: Vec<i32> = vec![1, 2, 3].into_iter().map(|n| n * 2).collect();
println!("{doubled:?}");filter keeps what passes
filter keeps only the items its test accepts:
let big: Vec<i32> = vec![1, 5, 2, 8].into_iter().filter(|n| *n > 3).collect();
println!("{big:?}");enumerate numbers the items
enumerate pairs each item with its position, counting from 0:
for (i, w) in ["a", "b"].iter().enumerate() {
println!("{i}: {w}");
}zip walks two iterators together
zip pairs items up side by side, and stops when the shorter side runs out:
let pairs: Vec<(&str, i32)> = ["a", "b"].iter().copied().zip([10, 20]).collect();
println!("{pairs:?}");Turn references into values with copied and cloned
Chains over .iter() carry references. copied() turns &T into T when T is Copy, like a number. cloned() clones each item for heavier types, like String:
let nums = vec![1, 2, 3];
let refs: Vec<&i32> = nums.iter().collect();
let vals: Vec<i32> = nums.iter().copied().collect();
println!("{refs:?} {vals:?}");
let words: Vec<String> = vec![String::from("a")].iter().cloned().collect();
println!("{words:?}");flatten: one level of nesting removed
The items here are arrays. flatten walks into each one and hands over its members, in order:
let grid = [[1, 2], [3, 4]];
let all: Vec<i32> = grid.iter().flatten().copied().collect();
println!("{all:?}");Example
fn main() {
let raw = ["12", "oops", "30", "-5", "7"];
let positives: Vec<i32> = raw.iter()
.filter_map(|s| s.parse::<i32>().ok())
.filter(|&n| n > 0)
.map(|n| n * 10)
.collect();
println!("{:?}", positives);
}Key points
- In short, an adapter wraps one iterator and returns a new iterator with one small change.
- Use flat_map(f) when each item becomes a small group, and you want one flat stream
- Use inspect(f) to peek at items mid-chain while debugging, without changing them
- Use chain(other) to run one iterator after another, as a single stream
- Use take(n) to keep only the first n items.
- Use skip(n) to drop the first n.
- Use step_by(n) to keep every nth item.
- Use rev() to walk backwards — it works on ranges, Vecs, and other double-ended iterators
- Adapters keep, change, or skip items.
- Chained adapters usually run as one tight loop.
In this example, raw holds five pieces of text. The chain has three steps. First, filter_map tries to parse each piece as a number. It keeps only successes: 12, 30, -5, 7. "oops" fails and quietly disappears. Next, filter keeps only numbers above 0, so -5 is dropped. Then map multiplies each survivor by 10. collect() does the pulling, and the result is [120, 300, 70]. All three steps ran fused into a single pass over the data.
Note
The adapters receive closures, from the Closures lesson.
Writing |n| n * 2 inside map is the everyday way to use them.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
Which adapter parses ["1", "x", "3"] and keeps only the values that parse?