Intermediate · Closures, iterators and smart pointers · topic 32 of 50 · 30 min
Iterators
Explanation
Why iterators?
Most data work is "go through a list, keep some items, change them, combine the result". You can write that with for loops and temporary variables, or describe it as a chain of small steps. The chain is often shorter, clearer and just as fast. You have already used iterators: every for loop uses one behind the scenes.
The Iterator trait
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
// ...plus dozens of provided methods: map, filter, sum, collect...
}
let v = vec![10, 20];
let mut it = v.iter();
println!("{:?}", it.next()); // Some(10)
println!("{:?}", it.next()); // Some(20)
println!("{:?}", it.next()); // None: finishedtype Item is an associated type: each iterator says what kind of item it produces. A for loop simply calls next() until it gets None.
Three ways to iterate a collection
| Method | Items are | Collection afterwards |
|---|---|---|
| v.iter() | &T (borrowed) | Still usable |
| v.iter_mut() | &mut T (can change) | Still usable, maybe changed |
| v.into_iter() | T (owned) | Moved; cannot be used |
Adapters: building the chain
An adapter takes an iterator and returns a new iterator. Adapters are lazy: they do no work until something asks for items. So a chain of map and filter does nothing on its own; the compiler even warns "unused iterator that must be used".
let prices = vec![120, 45, 300, 80, 999];
let with_gst: Vec<u32> = prices.iter()
.filter(|&&p| p >= 100) // keep items of Rs 100 or more
.map(|&p| p + p * 18 / 100) // add GST
.collect(); // run the chain into a Vec
println!("{:?}", with_gst); // [141, 354, 1178]| Adapter | What it does |
|---|---|
| map(f) | Transform each item |
| filter(f) | Keep items where f returns true |
| enumerate() | Pair each item with its index: (0, a), (1, b)... |
| zip(other) | Pair items from two iterators |
| take(n) / skip(n) | First n items / skip the first n |
| rev() | Go backwards (for iterators that support it) |
| filter_map(f) | Map and drop the None results in one step |
| chain(other) | One iterator after another |
Consumers: getting a result
let marks = [72, 91, 45, 88, 30];
let total: u32 = marks.iter().sum();
let passed = marks.iter().filter(|&&m| m >= 40).count();
let top = marks.iter().max(); // Some(91)
let any_fail = marks.iter().any(|&m| m < 33); // true
let first_80 = marks.iter().find(|&&m| m >= 80); // Some(91)
let product = (1..=5).fold(1, |acc, x| acc * x); // 120
println!("{total} {passed} {:?} {any_fail} {:?} {product}", top, first_80);collect() can build many collections: a Vec, a String, a HashMap from pairs, or a HashSet. Tell it which with a type annotation or the turbofish: collect::<Vec<_>>(). The underscore asks the compiler to fill in the item type.
Zero-cost abstraction
Rust's iterator chains are compiled into a plain loop with no extra allocations or function calls. The Rust Book shows benchmarks where the iterator version is as fast as, or slightly faster than, the hand-written loop, because the compiler can remove bounds checks. You can use the clear style without paying for it.
Common mistakes
- Writing a chain without a consumer, like v.iter().map(|x| x * 2); and wondering why nothing happened. Add collect, sum or a for loop.
- Confusing reference levels in closures (&x, &&x). Follow the compiler hints, or use .copied() to turn &u32 items into u32.
- Calling collect() without saying what to collect into: "type annotations needed".
- Using into_iter() and then trying to use the collection again.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Iterators · 1/6
An iterator hands out items one at a time. Its next method gives Some item, and finally None.
Prefer a video by a teacher? Find free videos on “rust iterators map filter collect fold tutorial” on YouTube ↗
In real life: Summarising a day of food orders
A cloud kitchen exports orders as (dish, quantity, price) records. One iterator chain filters out cancelled orders, computes each line total and sums revenue; another finds the best-selling dish. The code reads like the business question, and it runs as fast as manual loops.
fn main() {
let orders = vec![
("biryani", 2, 220, false),
("dosa", 3, 90, false),
("biryani", 1, 220, true), // cancelled
("paneer roll", 4, 120, false),
];
let revenue: u32 = orders.iter()
.filter(|o| !o.3)
.map(|o| o.1 * o.2)
.sum();
let best = orders.iter()
.filter(|o| !o.3)
.max_by_key(|o| o.1)
.map(|o| o.0);
println!("Revenue Rs {revenue}, best seller {:?}", best);
}Practice (0/3 done)
What does this print? let v = vec![1, 2, 3, 4, 5, 6]; let s: i32 = v.iter().filter(|&&x| x % 2 == 0).map(|x| x * 10).sum(); println!("{s}");
What happens here? let v = vec![1, 2, 3]; v.iter().map(|x| { println!("{x}"); x * 2 });
Given a Vec of (name, marks) tuples for a class, use iterator chains to: (1) count students who passed (40+), (2) get a Vec of names of students with 75+, (3) find the topper, (4) compute the class average with sum and len. No for loops allowed.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Box<T>: values on the heapClosures, iterators and smart pointers · same module
- Rc, RefCell and WeakClosures, iterators and smart pointers · same module
- Iterators and generatorsPython Programming · Advanced Python
- Iterators and the iteration protocolJavaScript Programming (language in depth) · Map, Set, iterators, generators and regular expressions
- Iterators: the glue between containers and algorithmsC++ Programming (modern C++ and DSA) · The Standard Template Library (STL)
- Vec<T>: growable listsError handling and collections
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What does next() return when the iterator is finished?
Answer: None
next returns None at the end.
Which method gives mutable references to items?
Answer: iter_mut()
iter_mut yields &mut T.
What does "lazy" mean for adapters like map?
Answer: They do nothing until a consumer asks for items
Work happens only when the chain is consumed.
What is (1..=4).fold(0, |acc, x| acc + x)?
Answer: 10
0 + 1 + 2 + 3 + 4 = 10.
Are iterator chains slower than for loops in Rust?
Answer: No, they compile to equally fast code
Iterators are a zero-cost abstraction in optimised builds.
What does next() do when an iterator has no items left?Related
Answer: Raises StopIteration
StopIteration is the signal that the iterator is finished. A for loop catches it for you.
What does an iterator's next() return?Related
Answer: An object { value, done }
next() returns { value, done }.
What does it->first give for a std::map iterator?Related
Answer: The key
Map elements are pairs: first is the key, second is the value.
Why does Rust forbid a &mut while & references exist?Related
Answer: So readers never see the value change under them (no data races)
Exclusive access for writers prevents data races and similar bugs.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 2 more topics in “Closures, iterators and smart pointers” before its exam: Box<T>: values on the heap, Rc, RefCell and Weak.
- Module 8 exam: Closures, iterators and smart pointers · 13 questions · 25 min, without help. 4 topics of this module still to finish first.
- Then the next module, Concurrency and async. 50 topics left in the course before the final exam.