Rust More Iterator Adapters
More adapters add memory, stopping, and repeating to an iterator chain. scan remembers a running value.
skip_while drops only the front run
Both lines start from the same numbers. skip_while drops the leading items above 3, then keeps everything — including the 9. filter drops every item above 3, wherever it sits:
let lead: Vec<i32> = vec![5, 1, 2, 9, 3].into_iter().skip_while(|n| *n > 3).collect();
println!("{lead:?}");
let rest: Vec<i32> = vec![5, 1, 2, 9, 3].into_iter().filter(|n| *n <= 3).collect();
println!("{rest:?}");map_while stops at the first failure
Parsing works until "x" — and the chain ends there. The "15" after it is never reached:
let parsed: Vec<i32> = ["4", "8", "x", "15"]
.iter()
.map_while(|s| s.parse::<i32>().ok())
.collect();
println!("{parsed:?}");scan keeps a running total
scan is map with a memory. The state starts at 0. Each step adds one item into it. The new total is the item handed on:
let running: Vec<i32> = vec![3, 1, 4, 1]
.iter()
.scan(0, |total, &n| {
*total += n;
Some(*total)
})
.collect();
println!("{running:?}");flatten removes one level
A Vec of Vecs becomes one flat stream of numbers:
let flat: Vec<i32> = vec![vec![1, 2], vec![3], vec![4, 5]].into_iter().flatten().collect();
println!("{flat:?}");fuse keeps a finished iterator finished
std::iter::from_fn builds an iterator from a closure. Here the closure is badly behaved. It wakes up with a 9 after returning None. fuse() seals it: after the first None, every next() is None:
let mut calls = 0;
let mut flaky = std::iter::from_fn(move || {
calls += 1;
match calls {
1 => Some(1),
2 => None,
_ => Some(9),
}
})
.fuse();
println!("{:?} {:?} {:?}", flaky.next(), flaky.next(), flaky.next());
println!("{:?}", flaky.next());Example
fn main() {
let readings = [2, 4, 6, 99, 8, 10, 12];
let kept: Vec<i32> = readings.iter().skip_while(|n| **n < 5).copied().collect();
println!("after warm-up = {kept:?}");
let parsed: Vec<i32> = ["3", "5", "oops", "7"]
.iter()
.map_while(|s| s.parse::<i32>().ok())
.collect();
println!("parsed = {parsed:?}");
let totals: Vec<i32> = parsed
.iter()
.scan(0, |run, &n| {
*run += n;
if *run > 6 { None } else { Some(*run) }
})
.collect();
println!("running totals = {totals:?}");
let grid = vec![vec![1, 2], vec![3, 4]];
let cells: Vec<i32> = grid.into_iter().flatten().collect();
println!("cells = {cells:?}");
let lights: Vec<&str> = ["red", "green"].into_iter().cycle().take(5).collect();
println!("lights = {lights:?}");
}Key points
- Use skip_while(test) to drop a leading run only.
- The first item that fails the test is kept, and everything after it too.
- That holds even for later items that would pass the test.
- Use map_while(f) to convert and stop.
- It works like filter_map, but the first None ends the iterator.
- The None is not skipped over.
- Use scan(state, f) for a running value.
- The closure sees &mut state and each item.
- It returns Some(next item) — or None to stop the chain.
- Use flatten() when the items are themselves collections or Options: one level of nesting is removed
- Use cycle() to repeat a finished iterator from the start, forever.
- Always pair it with take or another brake.
- Use fuse() to promise that after the first None the iterator stays finished.
- Adapters and library code may rely on that promise.
- flat_map maps and then flattens in one step.
- flatten only flattens what is already nested.
In this example, each new adapter does one job. skip_while drops the warm-up readings 2 and 4. The 6 fails the test. So it is kept, and everything after it too — even the small 8. map_while parses numbers and stops dead at "oops", so parsed is [3, 5] and the 7 is never tried. scan then walks those two numbers with a running total. 3 is handed on. But adding 5 would take the total to 8, past the limit of 6. So the closure returns None and the chain ends. Running totals is just [3]. Below, flatten pours the two rows of the grid into one stream of cells. cycle repeats the two traffic-light colours. take(5) acts as the brake.
Note
flat_map and flatten are the same idea split in two.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
What does [2, 4, 6, 1].skip_while(|n| *n < 5) produce?