Rust Custom Iterators
A custom iterator is a type that implements the Iterator trait for its own values. Only one method, next, does the required work.
Only two things to write
Name the item type, then write next — Some for each value, None at the end:
type Item = u32;
fn next(&mut self) -> Option<Self::Item>Adapters and consumers come free
Counter is the type built in the closing program below. Only next was written for it, yet map and collect work at once:
let doubled: Vec<u32> = Counter::new().map(|n| n * 2).collect();
println!("{doubled:?}");Endless is allowed — bring a brake
A generator may never end, like the Fibonacci one in the Size Hints lesson. The std range 0.. never ends either. skip and take cut a safe window out of it:
let middle: Vec<i32> = (0..).skip(4).take(4).collect();
println!("{middle:?}");Borrow the iterator with by_ref
Consumers take the iterator by value. by_ref borrows it instead. So the same Counter keeps its place after take(2) lets go. The last line reads the honest hint from the size_hint method above:
let mut counter = Counter::new();
let first_two: Vec<u32> = counter.by_ref().take(2).collect();
println!("first two = {first_two:?}");
println!("next = {:?}", counter.next());
println!("hint = {:?}", counter.size_hint());
let rest: Vec<u32> = counter.collect();
println!("rest = {rest:?}");Example
struct Counter { count: u32 }
impl Counter { fn new() -> Counter { Counter { count: 0 } } }
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
self.count += 1;
if self.count < 6 { Some(self.count) } else { None }
}
fn size_hint(&self) -> (usize, Option<usize>) {
let left = (5 - self.count) as usize;
(left, Some(left))
}
}
fn main() {
let mut counter = Counter::new();
println!("first = {:?}", counter.next());
let rest: Vec<u32> = counter.collect();
println!("rest = {rest:?}");
let total: u32 = Counter::new().zip(Counter::new().skip(1)).map(|(a, b)| a * b).sum();
println!("total = {total}");
}Key points
- Calling next() yourself needs the iterator variable to be mut.
- next changes the position inside.
- A for loop handles that mut for you behind the scenes.
- Give your iterator an honest size_hint when it can know what remains.
- Counter always knows.
- It reports (5 - count, Some(5 - count)).
- A size_hint must never lie.
- A lower bound above the true count breaks library code that trusted the promise.
- So does an upper bound below it.
- A Vec iterator is just a type.
- Its next() hands out the next element.
- There is no special magic reserved for std.
- Counters, readers, and generators can all be custom iterators.
- A custom iterator is a type that hands out its own values through the Iterator trait.
In this example, Counter counts upward. Its next() adds 1. Then it returns Some(count) while the count is under 6. It returns None at 6. So the sequence is 1, 2, 3, 4, 5. main calls next() directly once — counter must be mut for that — and prints Some(1). collect() then pulls the rest: [2, 3, 4, 5]. The last line shows the free tools. Two fresh counters are zipped together. One skips its first item. The pairs are multiplied: (1*2, 2*3, 3*4, 4*5). Then sum() adds them to 40. Only next() was written by hand. The Iterator Size Hints lesson covers the contract in full.
Note
The standard library iterators follow this exact contract.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
What is the only method you must write to implement Iterator for your own type?