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

Leave a Comment