The Iterator Trait
Iterator trait is the recipe every list-walker in Rust follows: it just needs to know how to hand back the next item.In this page:
impl Iterator for Name {
type Item = Type;
fn next(&mut self) -> Option<Self::Item> {
// body
}
}
Calling next Manually
Every iterator provides a next() method returning Some(item) while there are more elements, and None once exhausted. A for loop is just repeatedly calling next() under the hood.
Example: Calling next Manually
// Entry point: execution of the program starts here
fn main() {
// Declare a mutable variable `iter`, initialized to `vec![1, 2, 3].into_iter()`
let mut iter = vec![1, 2, 3].into_iter();
// Print "{:?}" to the console, substituting in `iter.next()` (with a trailing newline)
println!("{:?}", iter.next());
// Print "{:?}" to the console, substituting in `iter.next()` (with a trailing newline)
println!("{:?}", iter.next());
// Print "{:?}" to the console, substituting in `iter.next()` (with a trailing newline)
println!("{:?}", iter.next());
// Print "{:?}" to the console, substituting in `iter.next()` (with a trailing newline)
println!("{:?}", iter.next());
}
Login to try C/C++/Java/PHP code in the editor
Implementing a Custom Iterator
Implementing Iterator for your own type only requires defining next(); the associated Item type declares what kind of value the iterator produces.
Example: Implementing a Custom Iterator
// Define a struct `Countdown` to group related fields together
struct Countdown(u32);
// Implement the `Iterator` trait for `Countdown`
impl Iterator for Countdown {
type Item = u32;
// Define the function `next` taking `self` and returning a `Option<u32>`
fn next(&mut self) -> Option<u32> {
// Check whether `self.0 == 0`
if self.0 == 0 {
None
// Otherwise, run this branch
} else {
self.0 -= 1;
Some(self.0 + 1)
}
}
}
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `countdown`, set to `Countdown(3)`
let countdown = Countdown(3);
// Loop over `countdown`, binding each item to `n`
for n in countdown {
// Print "{}" to the console, substituting in `n` (with a trailing newline)
println!("{}", n);
}
}
Login to try C/C++/Java/PHP code in the editor
Free Adapter Methods from Iterator
Once a type implements Iterator by defining next(), it automatically gains access to many built-in adapter methods, such as .sum(), without any extra work.
Example: Free Adapter Methods from Iterator
// Define a struct `Countdown` to group related fields together
struct Countdown(u32);
// Implement the `Iterator` trait for `Countdown`
impl Iterator for Countdown {
type Item = u32;
// Define the function `next` taking `self` and returning a `Option<u32>`
fn next(&mut self) -> Option<u32> {
// Check whether `self.0 == 0`
if self.0 == 0 {
None
// Otherwise, run this branch
} else {
self.0 -= 1;
Some(self.0 + 1)
}
}
}
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `total` (an unsigned integer), set to `Countdown(4).sum()`
let total: u32 = Countdown(4).sum();
// Print "Sum: {}" to the console, substituting in `total` (with a trailing newline)
println!("Sum: {}", total);
}
Login to try C/C++/Java/PHP code in the editor
Iterators Are Lazy
Iterator adapters like .map() do not run their closure immediately -- they build a pipeline description that only executes once something consumes the iterator, such as .collect() or a for loop.
Example: Iterators Are Lazy
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `numbers`, set to `vec![1, 2, 3]`
let numbers = vec![1, 2, 3];
let lazy_iter = numbers.iter().map(|n| {
// Print "Processing {}" to the console, substituting in `n` (with a trailing newline)
println!("Processing {}", n);
n * 2
});
// Declare an immutable variable `result` (a `Vec<i32>`), set to `lazy_iter.collect()`
let result: Vec<i32> = lazy_iter.collect();
// Print "{:?}" to the console, substituting in `result` (with a trailing newline)
println!("{:?}", result);
}
Login to try C/C++/Java/PHP code in the editor
- Forgetting
.next()returns anOption<Item>, usingNoneto signal the iterator is exhausted. - Assuming iterators are eager -- most iterator adapters like
.map()are lazy and don't run until consumed. - Implementing
Iteratorbut forgetting it only requires definingnext(); every other method has a default implementation built on top of it.
- The
Iteratortrait requires just one method,next(&mut self) -> Option<Self::Item>. - Calling
.next()repeatedly yieldsSome(item)until the sequence is exhausted, then returnsNone. - Implementing
Iteratorfor a custom type automatically unlocks dozens of adapter methods like.map()and.filter(). - Iterators are lazy: adapters build up a pipeline that only actually runs when consumed, such as by
.collect()or aforloop.
Chapter Quiz — Complete all 6 topics to unlock
0/6 topics done
Complete these topics first: