FusedIterator — Why Fuse Matters
FusedIterator — Why Fuse Matters
Thesis
FusedIterator marks iterators that keep returning None forever after the first None. Adapters like fuse() wrap a flaky iterator so callers (and Iterator combinators) can assume fusion.
Mental model
Ordinary iterator: Some, Some, None, Some? ← "zombie" possible
Fused: Some, Some, None, None, None, …
Iterator::fuse returns Fuse<Self> implementing FusedIterator.
Worked example
// fused_demo.rs
use std::iter::{Fuse, FusedIterator};
struct OnceThenWeird {
state: u8,
}
impl Iterator for OnceThenWeird {
type Item = i32;
fn next(&mut self) -> Option<i32> {
self.state += 1;
match self.state {
1 => Some(10),
2 => None,
3 => Some(99), // zombie value — bad manners
_ => None,
}
}
}
fn main() {
let mut raw = OnceThenWeird { state: 0 };
println!("raw: {:?} {:?} {:?} {:?}", raw.next(), raw.next(), raw.next(), raw.next());
let mut fused: Fuse<_> = (OnceThenWeird { state: 0 }).fuse();
println!(
"fused: {:?} {:?} {:?} {:?}",
fused.next(),
fused.next(),
fused.next(),
fused.next()
);
fn assert_fused<I: FusedIterator>(_: &I) {}
assert_fused(&fused);
}first pass: [1, 2, 3]
second pass: []
Trap
Implementing FusedIterator for a type that can revive. The marker is a contract; lying breaks Flatten / other adapters that skip re-polling exhausted inners incorrectly if the contract fails. Prefer .fuse() at the boundary over claiming the trait yourself unless proven.
Boring rule
If you hand an iterator to combinators after a possible early None, call .fuse(). Implement FusedIterator only when your next is proven fused.
Try this
- Read the
Fusesource — it stores a flag after firstNone. - Check which std iterators already implement
FusedIterator. - Break
Flattenintentionally with a zombie iterator (in a toy) to feel why fusion matters.