Rust Lazy Iterators
A lazy iterator does no work until a consumer pulls an item. A consumer may stop early and leave the rest untouched.
Nothing runs until you pull
Build a chain and store it. The inspect flag inside would print a line — but nothing prints until next() is called:
let mut chain = (1..=4).inspect(|n| println!("saw {n}"));
println!("built");
println!("pulled = {:?}", chain.next());One item at a time
Two flags, one before map and one after it. Watch the order — the flags take turns, item by item. Rust does not run all of map first and hand over a finished list:
let doubled: Vec<i32> = (1..=3)
.inspect(|n| println!("in {n}"))
.map(|n| n * 2)
.inspect(|n| println!("out {n}"))
.collect();
println!("{doubled:?}");A search stops the chain
find pulls only until its answer turns up. The counter proves the chain never looked at the other 96 numbers:
let mut looked = 0;
let hit = (1..=100).inspect(|_| looked += 1).find(|n| *n == 4);
println!("hit = {hit:?} looked = {looked}");Example
fn main() {
let mut searched = 0;
let first_big = (1..)
.inspect(|_| searched += 1)
.find(|n| *n * *n > 500);
println!("first = {:?}", first_big);
println!("searched = {searched}");
let chain = (1..=10)
.inspect(|n| println!("in {n}"))
.filter(|n| n % 2 == 0)
.inspect(|n| println!("kept {n}"))
.take(2);
println!("built, nothing ran yet");
let evens: Vec<i32> = chain.collect();
println!("evens = {evens:?}");
}Key points
- Building a chain runs nothing: no closure is called until a consumer pulls
- Items travel one at a time: each item passes through every step before the next item starts
- A consumer may stop early.
- find and take leave the rest of the chain untouched.
- Even an endless range is safe behind them.
- Use inspect(f) to watch items flow past a point in the chain.
- It changes nothing.
- So it is the everyday debugging tool for pipelines.
In this example, the first chain searches the endless range 1.. for the first number whose square passes 500. The inspect counter rises once per pull. find stops the moment 23 appears. That is 23 pulls, searched = 23. And the endless range causes no trouble, because nobody pulls further. The second chain is only built at first. "built, nothing ran yet" prints before any flag. collect has not pulled yet. Once collect pulls, the flags interleave: in 1, in 2, kept 2, in 3, in 4, kept 4. Then take(2) ends the chain after the second kept item. So items 5 to 10 are never touched.
Note
Rust warns you about a forgotten chain.
An adapter result nobody consumes draws an "unused must be used" warning.
A chain with no consumer does nothing at all.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
You build a chain with inspect inside it, but never call collect, sum, find or a loop on it. What prints?