Skip to content

Rust Iterators

An iterator is anything that can produce a sequence of values, one at a time. You’ve been using them since the For page without necessarily naming them — every for loop over a collection is powered by an iterator behind the scenes.

fn main() {
let numbers = vec![1, 2, 3];
let mut iter = numbers.iter();
println!("{:?}", iter.next()); // Some(1)
println!("{:?}", iter.next()); // Some(2)
println!("{:?}", iter.next()); // Some(3)
println!("{:?}", iter.next()); // None
}
▶ Try it Yourself

.next() returns an OptionSome(value) for each item, and None once it runs out. A for loop is really just calling .next() repeatedly under the hood until it gets None.

.map() takes a closure and applies it to every item, producing a new iterator:

fn main() {
let numbers = vec![1, 2, 3, 4, 5];
let doubled: Vec<i32> = numbers.iter().map(|n| n * 2).collect();
println!("{:?}", doubled);
}

Output:

[2, 4, 6, 8, 10]
▶ Try it Yourself

.collect() is what turns the iterator back into a concrete collection (here, a Vec<i32>) — without it, you’d just have an unevaluated iterator sitting there.

fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6];
let evens: Vec<&i32> = numbers.iter().filter(|n| **n % 2 == 0).collect();
println!("{:?}", evens);
}

Output:

[2, 4, 6]
▶ Try it Yourself

The double **n looks unusual — .filter() hands the closure a reference to a reference (&&i32), so it needs dereferencing twice to compare against a plain number. It’s a common little wrinkle; don’t worry about the full explanation yet.

This is where chaining iterator methods starts to pay off — you can combine several steps into one readable pipeline:

fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6];
let result: Vec<i32> = numbers
.iter()
.filter(|n| **n % 2 == 0) // keep even numbers
.map(|n| n * 10) // multiply each by 10
.collect();
println!("{:?}", result);
}

Output:

[20, 40, 60]
▶ Try it Yourself

Compare this to writing the same logic with a manual for loop and an empty Vec you push into — it’s not wrong, but the chained version reads closer to “filter, then multiply, then collect,” which is exactly the sequence of steps you’re describing.

fn main() {
let numbers = vec![1, 2, 3, 4, 5];
let total: i32 = numbers.iter().sum();
let count = numbers.iter().count();
let has_even = numbers.iter().any(|n| n % 2 == 0);
println!("sum: {total}, count: {count}, has even: {has_even}");
}
▶ Try it Yourself
  • An iterator produces values one at a time via .next(), which returns an Option.
  • for loops are iterators under the hood.
  • .map() transforms each item; .filter() keeps only the ones matching a condition; .collect() turns the result back into a concrete collection.
  • Chaining iterator methods together is usually clearer than a manual loop with a mutable accumulator.