🎓 A1Learner
🔍

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?