Rust Iterators and Adapters
Master Rust's iterator trait, lazy adapters like map and filter, and consumers like collect and fold for fast, expressive data processing.
What you'll learn
- ✓The Iterator trait
- ✓Lazy adapters
- ✓Consumers
- ✓Custom iterators
- ✓Performance characteristics
Prerequisites
- •Basic familiarity with Rust
What and Why
Iterators are how Rust handles sequences. Instead of indexed for-loops, you build pipelines of small operations. The compiler then fuses them into tight loops via monomorphization, so high-level code generates the same assembly you’d write by hand.
The whole system is built on one trait, Iterator, with one required method, next. Everything else is default methods or adapter types built around that.
Mental Model
An iterator is a lazy stream. Adapters like map, filter, and take return new iterators that wrap the original. Nothing runs until a consumer like collect, sum, for_each, or a for loop pulls values through. This laziness lets the compiler optimize away the intermediate types.
vec.iter() --> Iter<i32>
.filter(...) --> Filter<Iter<i32>, F>
.map(...) --> Map<Filter<...>, G>
.sum::<i32>() --> i32 (runs the pipeline) Hands-on Example
fn main() {
let nums = vec![1, 2, 3, 4, 5, 6];
let sum_of_squares: i32 = nums.iter()
.filter(|&&n| n % 2 == 0)
.map(|&n| n * n)
.sum();
println!("{}", sum_of_squares); // 56
// Build a HashMap
use std::collections::HashMap;
let lengths: HashMap<&str, usize> = ["apple", "fig", "banana"]
.iter()
.map(|s| (*s, s.len()))
.collect();
// Chain and enumerate
for (i, x) in nums.iter().chain(&[100, 200]).enumerate() {
println!("{i}: {x}");
}
}
Three flavors of iterator from a collection: iter() (by reference), iter_mut() (mutable reference), and into_iter() (by value, consuming).
Custom Iterator
struct Fib { a: u64, b: u64 }
impl Iterator for Fib {
type Item = u64;
fn next(&mut self) -> Option<u64> {
let next = self.a;
self.a = self.b;
self.b = next + self.b;
Some(next)
}
}
fn main() {
let fibs: Vec<u64> = Fib { a: 0, b: 1 }.take(10).collect();
println!("{:?}", fibs);
}
Implementing Iterator once unlocks every adapter automatically.
Common Pitfalls
Forgetting iterators are lazy. nums.iter().map(|x| println!("{x}")); prints nothing because no consumer ever pulls. Add .for_each(|_| {}) or just use a for loop.
Borrow vs ownership confusion. for x in vec consumes the vector; for x in &vec borrows. Adapters compose from whichever you start with.
collect type annotations. let v = ... .collect(); doesn’t compile without telling the compiler what to collect into. Use .collect::<Vec<_>>() or annotate the binding type.
Overusing clone. A common reflex is sprinkling .cloned() in pipelines. Often you can pass references through and avoid the copy.
Allocation in hot loops. .collect::<Vec<_>>() between two steps allocates. Keep the pipeline going if you can; only collect at the boundaries.
Practical Tips
- Use
iter()for read-only,iter_mut()to modify in place,into_iter()when you no longer need the collection. - Prefer
find,any,allover manual loops for early exit. flat_mapis your friend for unflattening one-to-many transformations.foldandtry_foldexpress many algorithms cleanly;try_foldshort-circuits onErrorNone.- For numeric work,
sum,product,min,maxare zero-cost over a typed iterator. - Reach for the
itertoolscrate when you need windows, group-by, or unzipping multiple ways.
Wrap-up
Iterators are Rust’s superpower for data processing: expressive, safe, and zero-cost. Start by replacing loops with map/filter/collect, then learn the consumers, then implement your own when needed. The same pipeline reads like prose and compiles to a single tight loop, which is the kind of trade-off Rust was designed to make easy.
Related articles
- Rust Rust Iterators and Closures: Functional Programming
Master Rust iterators and closures for functional-style programming. Covers map, filter, fold, chaining, custom iterators, and Fn trait family.
- Rust Trait Objects vs Generics in Rust: A Decision Guide
Understand the trade-offs between dynamic dispatch with trait objects and static dispatch with generics in Rust through real-world examples.
- Rust Traits and Generics in Rust
Master Rust traits and generics — defining traits, trait bounds, default methods, trait objects, and generic data structures.
- Rust Rust Deref and AsRef Explained
Demystify Rust's Deref, DerefMut, and AsRef traits with clear examples showing how smart pointers, coercions, and flexible APIs really work.