Iterators
Introduction#
Iterators are a powerful language feature in Rust, described by the Iterator
trait. Iterators allow you to perform many operations on collection-like types, e.g. Vec<T>
, and they are easily composable.
Adapters and Consumers
Iterator methods can be broken into two distinct groups:
Adapters
Adapters take an iterator and return another iterator
// Iterator Adapter
// | |
let my_map = (1..6).map(|x| x * x);
println!("{:?}", my_map);
Output
Map { iter: 1..6 }
Note that the values were not enumerated, which indicates that iterators are not eagerly evaluated — iterators are “lazy”.
Consumers
Consumers take an iterator and return something other than an iterator, consuming the iterator in the process.
// Iterator Adapter Consumer
// | | |
let my_squares: Vec<_> = (1..6).map(|x| x * x).collect();
println!("{:?}", my_squares);
Output
[1, 4, 9, 16, 25]
Other examples of consumers include find
, fold
, and sum
.
let my_squared_sum: u32 = (1..6).map(|x| x * x).sum();
println!("{:?}", my_squared_sum);
Output
55
A short primality test
fn is_prime(n: u64) -> bool {
(2..n).all(|divisor| n % divisor != 0)
}
Of course this is isn’t a fast test. We can stop testing at the square root of n
:
(2..n)
.take_while(|divisor| divisor * divisor <= n)
.all(|divisor| n % divisor != 0)
Custom iterator
struct Fibonacci(u64, u64);
impl Iterator for Fibonacci {
type Item = u64;
// The method that generates each item
fn next(&mut self) -> Option<Self::Item> {
let ret = self.0;
self.0 = self.1;
self.1 += ret;
Some(ret) // since `None` is never returned, we have an infinite iterator
}
// Implementing the `next()` method suffices since every other iterator
// method has a default implementation
}
Example use:
// the iterator method `take()` is an adapter which limits the number of items
// generated by the original iterator
for i in Fibonacci(0, 1).take(10) {
println!("{}", i);
}