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}Output:
Some(1)Some(2)Some(3)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}");}Output:
sum: 15, count: 5, has even: true.sum() adds everything up, .count() tells you how many items there are, and .any() answers “does at least one item match?”. These are just a few of the methods every iterator has. There are many more, like enumerate, zip, take and rev. On the Custom Iterators page you’ll build your own iterator and see that it gets all of these methods for free.
Practice
Section titled “Practice”1. Sum of odd squares
Section titled “1. Sum of odd squares”Take the numbers 1 to 10, keep only the odd ones, square each one, and add them all up. Do it in one iterator chain, with no mut variable. Hint: chain .filter(), .map() and .sum(), and give the result a type like let total: i32.
Show solution
fn main() { let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let total: i32 = numbers .iter() .filter(|n| **n % 2 == 1) // keep 1, 3, 5, 7, 9 .map(|n| n * n) // 1, 9, 25, 49, 81 .sum();
println!("sum of odd squares: {total}");}Output:
sum of odd squares: 165Rust needs the : i32 on total because .sum() can add up into several number types, and it can’t guess which one you want.
2. Shout the long words
Section titled “2. Shout the long words”Start with vec!["the", "quick", "brown", "fox", "jumps"]. Build a new Vec<String> that has only the words longer than 3 letters, in uppercase, and print it with {:?}. Hint: .filter(|w| w.len() > 3) keeps the long words, and .to_uppercase() turns a &str into an uppercase String.
Show solution
fn main() { let words = vec!["the", "quick", "brown", "fox", "jumps"];
let loud: Vec<String> = words .iter() .filter(|w| w.len() > 3) .map(|w| w.to_uppercase()) .collect();
println!("{:?}", loud);}Output:
["QUICK", "BROWN", "JUMPS"]words itself is unchanged. .iter() only borrows it, so you can still use words after the chain.
- 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.
Quick check
Section titled “Quick check”Quick check
1. What does .next() return on an iterator?
2. What does .collect() do at the end of a chain like .iter().map(...).collect()?
3. In .filter(|n| **n % 2 == 0), why is n dereferenced twice?
4. What powers a for loop when it loops over a collection?
5. What does .filter() keep in the resulting iterator?
Score: 0 / 5
Read more
Section titled “Read more”- Processing a Series of Items with Iterators — The Rust Book: the official introduction to
next,map,filterandcollect, with more examples. - Comparing Performance: Loops vs. Iterators — The Rust Book: why iterator chains are just as fast as a hand-written loop.
Iteratortrait — standard library docs: the full list of iterator methods, each with a small example. Great to browse.- Iterators — Rust by Example: a short, runnable example that builds a Fibonacci iterator.