Iterators & Closures
Closures are small anonymous functions that capture their environment; iterators are lazy sequences that transform collections. Chained together they replace most hand-written loops with expressive, zero-cost pipelines.
Closures
A closure is a function without a name that can capture variables from its surroundings. The || holds parameters. Rust assigns one of three traits depending on how the closure captures:
| Trait | Captures by | Typical use |
|---|---|---|
Fn | immutable reference | read-only, call many times |
FnMut | mutable reference | may mutate captured state |
FnOnce | value (moves) | consumes capture, call once |
fn main() {
let prefix = String::from("Hello, ");
// captures `prefix` by reference (Fn trait)
let greet = |name: &str| format!("{prefix}{name}");
println!("{}", greet("Rust"));
let mut count = 0;
// captures `count` mutably (FnMut trait)
let mut inc = || { count += 1; count };
println!("{} {} {}", inc(), inc(), inc());
}
Iterators
An iterator is a lazy sequence: nothing runs until you consume it. Call .iter() (borrow), .into_iter() (own), or .iter_mut() (mutable) to obtain one, then drive it with a for loop:
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
for n in numbers.iter() { // borrow each element
print!("{n} ");
}
println!();
for n in numbers.iter().rev() { // reverse without allocating
print!("{n} ");
}
println!();
}
Combinators
Adapter methods transform one iterator into another lazily. Chain map, filter, and friends, then materialize the result only at the end with collect:
fn main() {
// square, keep even, sum the whole pipeline
let total: i32 = (1..=10)
.filter(|&n| n % 2 == 0) // keep evens
.map(|n| n * n) // square them
.sum(); // consume (adds up)
println!("sum of even squares = {total}");
let doubled: Vec = (0..5).map(|n| n * 2).collect();
println!("{doubled:?}");
}
Consuming Adapters
Adapters like fold, count, any, all, and max run the whole iteration and return a single value:
fn main() {
let words = ["the", "quick", "brown", "fox"];
let total_len: usize = words.iter().map(|w| w.len()).sum();
let has_q = words.iter().any(|&w| w.contains('q'));
let longest = words.iter().max_by_key(|w| w.len());
println!("total len = {total_len}, has 'q' = {has_q}");
println!("longest = {:?}", longest);
}
Practice move: rewrite a
for loop from the lab examples as a map/filter/collect chain and confirm the output is identical.