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.
Getting an iterator
Section titled “Getting an iterator”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}.next() returns an Option — Some(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 — transform every item
Section titled “map — transform every item”.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].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.
filter — keep only some items
Section titled “filter — keep only some items”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]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.
Chaining map and filter together
Section titled “Chaining map and filter together”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]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.
A few other common methods
Section titled “A few other common methods”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}");}- An iterator produces values one at a time via
.next(), which returns anOption. forloops 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.