Building Custom Iterators
In this page:
A Custom Iterator Struct
A custom iterator is typically a small struct holding whatever state is needed to compute the next item, with Iterator implemented on top of it.
Example: A Custom Iterator Struct
struct Evens {
current: i32,
max: i32,
}
impl Iterator for Evens {
type Item = i32;
fn next(&mut self) -> Option<i32> {
if self.current > self.max {
None
} else {
let value = self.current;
self.current += 2;
Some(value)
}
}
}
fn main() {
let evens = Evens { current: 0, max: 8 };
for n in evens {
println!("{}", n);
}
}
Login to try C/C++/Java/PHP code in the editor
Using Adapters on a Custom Iterator
Once Iterator is implemented, a custom type automatically supports the full range of adapter methods, letting you build pipelines just as you would on a built-in iterator.
Example: Using Adapters on a Custom Iterator
struct Evens {
current: i32,
max: i32,
}
impl Iterator for Evens {
type Item = i32;
fn next(&mut self) -> Option<i32> {
if self.current > self.max {
None
} else {
let value = self.current;
self.current += 2;
Some(value)
}
}
}
fn main() {
let total: i32 = Evens { current: 0, max: 10 }.map(|n| n * 2).sum();
println!("Total: {}", total);
}
Login to try C/C++/Java/PHP code in the editor
Implementing IntoIterator for a Collection
Implementing IntoIterator for a custom collection struct allows it to be used directly with a for loop, delegating to an inner iterator type.
Example: Implementing IntoIterator for a Collection
struct Bag {
items: Vec<i32>,
}
impl IntoIterator for Bag {
type Item = i32;
type IntoIter = std::vec::IntoIter<i32>;
fn into_iter(self) -> Self::IntoIter {
self.items.into_iter()
}
}
fn main() {
let bag = Bag { items: vec![10, 20, 30] };
for item in bag {
println!("{}", item);
}
}
Login to try C/C++/Java/PHP code in the editor
Ensuring the Iterator Terminates
A correctly implemented iterator must eventually return None; otherwise, any code that consumes it fully (like .collect() or .sum()) will loop forever.
Example: Ensuring the Iterator Terminates
struct Countdown(u32);
impl Iterator for Countdown {
type Item = u32;
fn next(&mut self) -> Option<u32> {
if self.0 == 0 {
None
} else {
self.0 -= 1;
Some(self.0 + 1)
}
}
}
fn main() {
let collected: Vec<u32> = Countdown(5).collect();
println!("{:?}", collected);
}
Login to try C/C++/Java/PHP code in the editor
- Forgetting to implement
IntoIteratorfor a collection struct if you want it to work directly in aforloop. - Writing
next()in a way that never returnsNone, causing any consuming method like.collect()to loop forever. - Reimplementing common adapter logic manually instead of just implementing
Iteratoronce and reusing the built-in adapter methods.
- Implementing
Iteratorfor a custom struct requires definingtype Itemand thenext()method. - Implementing
IntoIteratorlets a custom collection type be used directly in aforloop. - A well-behaved iterator must eventually return
Nonefromnext(), or consumers like.collect()will loop forever. - Custom iterators automatically gain access to the full standard library of iterator adapter methods.
Chapter Quiz — Complete all 6 topics to unlock
0/6 topics done
Complete these topics first: