Rust Iterators
An iterator is an object that produces a sequence of values one at a time. Instead of writing loops manually, iterators let you express what you want to do with a collection in a clean, readable way. Rust's iterator system is both powerful and efficient — iterator chains compile to code as fast as hand-written loops.
The Iterator Trait
All iterators implement the Iterator trait from the standard library. The only required method is next(), which returns the next value wrapped in Option, or None when the sequence is exhausted.
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
The Vending Machine Diagram
Iterator = A vending machine with items inside.
next() = Press the button once to get one item.
Press once → Some("apple")
Press again → Some("banana")
Press again → Some("cherry")
Press again → None (machine is empty)
Creating an Iterator
Call .iter() on a collection to get an iterator over it:
let fruits = vec!["apple", "banana", "cherry"];
let mut it = fruits.iter();
println!("{:?}", it.next()); ← Some("apple")
println!("{:?}", it.next()); ← Some("banana")
println!("{:?}", it.next()); ← Some("cherry")
println!("{:?}", it.next()); ← None
Three Ways to Iterate a Vector
v.iter() ← Produces &T (references, v unchanged after) v.iter_mut() ← Produces &mut T (mutable references, modify in place) v.into_iter() ← Produces T (takes ownership, v gone after)
Iterator Adapters
Adapters transform one iterator into another. They are lazy — they do no work until you consume the iterator.
map — Transform Each Value
let numbers = vec![1, 2, 3];
let doubled: Vec<i32> = numbers.iter().map(|&n| n * 2).collect();
println!("{:?}", doubled); ← [2, 4, 6]
filter — Keep Matching Values
let numbers = vec![1, 2, 3, 4, 5, 6];
let evens: Vec<i32> = numbers.iter().filter(|&&n| n % 2 == 0).collect();
println!("{:?}", evens); ← [2, 4, 6]
enumerate — Add Index to Each Item
let colors = vec!["red", "green", "blue"];
for (i, color) in colors.iter().enumerate() {
println!("{}: {}", i, color);
}
Output:
0: red 1: green 2: blue
zip — Combine Two Iterators
let names = vec!["Alice", "Bob"];
let scores = vec![95, 82];
for (name, score) in names.iter().zip(scores.iter()) {
println!("{} scored {}", name, score);
}
take and skip
let numbers = vec![1, 2, 3, 4, 5];
let first_three: Vec<i32> = numbers.iter().take(3).copied().collect();
let last_two: Vec<i32> = numbers.iter().skip(3).copied().collect();
println!("{:?}", first_three); ← [1, 2, 3]
println!("{:?}", last_two); ← [4, 5]
Consuming Adapters
Consuming adapters use up the iterator and produce a final result. They trigger the lazy chain to actually run.
collect — Build a Collection
let squares: Vec<i32> = (1..=5).map(|n| n * n).collect();
println!("{:?}", squares); ← [1, 4, 9, 16, 25]
sum and product
let total: i32 = vec![1, 2, 3, 4, 5].iter().sum();
println!("{}", total); ← 15
count
let big_count = vec![1, 5, 3, 8, 2].iter().filter(|&&n| n > 4).count();
println!("{}", big_count); ← 2
any and all
let numbers = vec![1, 2, 3, 4, 5];
println!("{}", numbers.iter().any(|&n| n > 4)); ← true (5 exists)
println!("{}", numbers.iter().all(|&n| n > 0)); ← true (all positive)
Chaining Adapters
Chain multiple adapters together to build a processing pipeline:
let result: Vec<i32> = (1..=10)
.filter(|n| n % 2 == 0) ← Keep even numbers
.map(|n| n * n) ← Square each one
.take(3) ← Take only the first 3
.collect();
println!("{:?}", result); ← [4, 16, 36]
The Pipeline Diagram
Range(1..=10) → filter(even) → map(square) → take(3) → collect
[1,2,3,4,5,6,7,8,9,10]
→ [2,4,6,8,10]
→ [4,16,36,64,100]
→ [4,16,36]
→ Vec: [4, 16, 36]
Creating Custom Iterators
Implement the Iterator trait on your own struct to create a custom sequence:
struct Counter {
count: u32,
}
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
self.count += 1;
if self.count <= 5 {
Some(self.count)
} else {
None
}
}
}
fn main() {
let counter = Counter { count: 0 };
let total: u32 = counter.sum();
println!("{}", total); ← 15
}
Quick Reference
v.iter() ← Start iterating (borrows) .map(|x| x * 2) ← Transform each element .filter(|&x| x > 0) ← Keep matching elements .enumerate() ← Add (index, value) pairs .take(n) ← First n elements .skip(n) ← Skip first n elements .collect::<Vec<_>>() ← Build a vector .sum::<i32>() ← Add all together .count() ← Count elements .any(|x| ...) ← True if any match .all(|x| ...) ← True if all match
