Iterator fusion
- Rust course
- Expert
- Lesson 15/46
- 4 min
Optimize with iterator fusion.
Iterator fusion
Chained adapters don't make separate passes: each element goes through the whole chain in one go. Through inlining, the compiler "fuses" the chain into a single loop. This is clearly visible when you observe the order:
let kayit = RefCell::new(Vec::new());
let _ = (1..=3)
.map(|x| { kayit.borrow_mut().push(format!("m{x}")); x * 2 })
.filter(|x| { kayit.borrow_mut().push(format!("f{x}")); *x > 2 })
.count();
// m1 f2 m2 f4 m3 f6 — element by element, not all maps firstSince no intermediate collection is created, memory use is constant too.
collect in between
Putting a collect in the middle of a chain breaks the fusion: all elements are written to a vector, then read again. Use an intermediate collection only when really needed (sorting, multiple passes):
// a needless intermediate vector
let ara: Vec<_> = v.iter().map(|x| x * 2).collect();
let t: i32 = ara.iter().filter(|x| **x > 2).sum();
// fused
let t: i32 = v.iter().map(|x| x * 2).filter(|x| *x > 2).sum();Practice Observe the fusion
Write the example above: add mX inside map and fX inside filter to a RefCell<Vec<String>> log; consume the chain with count(). Print the log joined with spaces.
Starter code
use std::cell::RefCell;
fn main() {
// buraya yaz
}Sample solution
use std::cell::RefCell;
fn main() {
let kayit = RefCell::new(Vec::new());
let _adet = (1..=3)
.map(|x| {
kayit.borrow_mut().push(format!("m{x}"));
x * 2
})
.filter(|x| {
kayit.borrow_mut().push(format!("f{x}"));
*x > 2
})
.count();
println!("{}", kayit.borrow().join(" "));
}Expected output
m1 f2 m2 f4 m3 f6
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 Why was <code>RefCell</code> needed for the log?
- It wasn't needed
- For threads
- Two closures want to change the same vector; two &mut can't be captured at once Correct answer
- For speed
Explanation: Each closure only captures &RefCell; the change is made at runtime, with short borrows.
2 Shared log
let kayit = ____::new(Vec::new());
// iki closure da kayit.borrow_mut() ile yazar
let kayit = RefCell::new(Vec::new());
// iki closure da kayit.borrow_mut() ile yazar
Explanation: RefCell lets two closures change the same vector in turn.
3 Chain and number of passes
| Term | Meaning |
|---|---|
map + filter + sum |
1 pass |
map + collect + filter + sum |
2 passes |
Work that needs sorting |
An intermediate collection is required |
take(2) |
Stops early |