Iterator scan and peekable
scan carries mutable state while yielding a new iterator, and peekable buffers one next item so code can inspect it without consuming it.
What it is
Iterator::scan is a stateful adapter.
It is like fold because it owns an accumulator-like state.
It differs from fold because it yields zero or more output items instead of returning one final value.
The scan closure receives &mut state and an input item.
It returns Some(output) to yield a value or None to stop the scan.
Iterator::peekable wraps an iterator in Peekable.
Peekable::peek returns a shared reference to the next item without advancing past the buffered item.
Peekable::peek_mut can mutate the buffered next item before it is yielded.
Together these adapters cover parsers, running totals, prefix detection, and lookahead.
They are useful when plain map and filter do not have enough memory of previous or upcoming items.
How it works
scan(initial_state, closure) stores initial_state inside the adapter.
Each next call pulls one upstream item, passes a mutable state reference to the closure, and handles the returned Option.
Returning None ends the scan.
The state remains private to the adapter.
This avoids external mutable variables in many pipelines.
peekable() stores an optional buffered item.
The first call to peek may call next on the underlying iterator to fill that buffer.
That means side effects of the underlying iterator’s next can happen during peek.
Repeated peek calls return the same buffered item until next consumes it.
After the buffered item is consumed, the next peek can pull again.
Use peekable when one-item lookahead is enough.
Example
fn main() {
let running: Vec<i32> = [1, 2, 3, 4]
.into_iter()
.scan(0, |sum, n| {
*sum += n;
Some(*sum)
})
.collect();
assert_eq!(running, [1, 3, 6, 10]);
}Edge cases
fn main() {
let mut chars = "12+34".chars().peekable();
let mut first_number = String::new();
while let Some(next) = chars.peek() {
if next.is_ascii_digit() {
first_number.push(chars.next().unwrap());
} else {
break;
}
}
assert_eq!(first_number, "12");
assert_eq!(chars.next(), Some('+'));
}Best practice
- ✅ Use
scanfor running state that still needs to yield intermediate values. - ✅ Use
foldwhen only the final state is needed. - ✅ Use
peekablefor one-token lookahead in small parsers. - ✅ Remember that
peekreturns a reference to the next item. - ✅ Use
peek_mutsparingly and only when mutating the buffered item is clearer than mapping. - ✅ Keep scan state small and local to the pipeline.
- ✅ Return
Nonefromscanto stop when a boundary is reached. - ✅ Prefer explicit parser code when lookahead grows beyond one item.
Pitfalls
- ⚠️
peekcan advance the underlying iterator enough to fill the buffer. - ⚠️
peekableprovides one-item lookahead, not arbitrary backtracking. - ⚠️ Returning
Nonefromscanends the adapter. - ⚠️ External mutable captures in
scancan fight the clarity of the internal state. - ⚠️ Calling
unwrapafterpeekcan still be fragile if control flow changes; see Unwrap and Expect Overuse. - ⚠️ Mutating with
peek_mutcan surprise readers if the later yielded value changes. - ⚠️ Using
scanfor simple stateless transforms obscures intent compared withmap. - ⚠️ Infinite iterators remain infinite unless
scanreturnsNoneor a later bound is applied.
See also
std: Iterator Adapter Catalog · Iterator fold and reduce · Iterator take skip and while bounds · Iterator map and filter · Iterator predicate search adapters · Iterator Adapters · Lazy Evaluation · Option · Mutable References · Closures · Unwrap and Expect Overuse
Sources
- Rust standard library,
Iterator::scan- std, https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.scan - Rust standard library,
Iterator::peekable- std, https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.peekable - Rust standard library,
Peekable- std, https://doc.rust-lang.org/std/iter/struct.Peekable.html
