| name | rust-closures-iterators |
| description | Master closures and iterators in Rust. Use when writing functional-style code, implementing custom iterators, using iterator adaptors, understanding Fn/FnMut/FnOnce traits, or optimizing iterator chains. |
Closures & Iterators
Based on The Rust Programming Language Ch. 13, Effective Rust, and the Iterator trait documentation.
When to Use This Skill
- Writing closures with correct capture semantics
- Understanding
Fn, FnMut, FnOnce trait hierarchy
- Chaining iterator adaptors for data transformation
- Implementing
Iterator for custom types
- Using
collect() effectively
- Performance of iterator chains vs loops
Closures
Capture Modes
let name = String::from("Alice");
let numbers = vec![1, 2, 3];
let print = || println!("{name}");
let mut acc = 0;
let mut add = |x| { acc += x; };
let consume = move || drop(numbers);
Fn Trait Hierarchy
FnOnce (can be called once — may consume captured values)
↑
FnMut (can be called multiple times — may mutate captures)
↑
Fn (can be called multiple times — only reads captures)
Every Fn is also FnMut and FnOnce. Compiler picks the least restrictive trait.
Closure Types in APIs
fn apply<F: FnOnce() -> T, T>(f: F) -> T { f() }
fn repeat<F: Fn() -> T, T>(f: F, n: usize) -> Vec<T> {
(0..n).map(|_| f()).collect()
}
fn fold<F: FnMut(Acc, Item) -> Acc>(mut f: F, init: Acc, items: &[Item]) -> Acc { ... }
move Closures
Force ownership transfer (required for threads, async):
let data = vec![1, 2, 3];
let handle = std::thread::spawn(move || {
println!("{data:?}");
});
Returning Closures
fn make_adder(x: i32) -> impl Fn(i32) -> i32 {
move |y| x + y
}
fn get_transform(kind: &str) -> Box<dyn Fn(i32) -> i32> {
match kind {
"double" => Box::new(|x| x * 2),
"negate" => Box::new(|x| -x),
_ => Box::new(|x| x),
}
}
Iterators
The Iterator Trait
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
Creating Iterators
let v = vec![1, 2, 3];
v.iter()
v.iter_mut()
v.into_iter()
(0..10)
(0..=10)
('a'..='z')
Key Adaptors (Lazy — No Work Until Consumed)
let result: Vec<_> = items.iter()
.filter(|x| x.is_valid())
.map(|x| x.transform())
.take(10)
.skip(2)
.enumerate()
.flat_map(|(i, x)| x.children())
.chain(extra.iter())
.zip(other.iter())
.inspect(|x| println!("{x:?}"))
.collect();
Consuming Methods (Drive the Iterator)
iter.count()
iter.sum::<i32>()
iter.product::<i32>()
iter.min() / iter.max()
iter.any(|x| predicate(x))
iter.all(|x| predicate(x))
iter.find(|x| x.id == 5)
iter.position(|x| x > 10)
iter.fold(init, |acc, x| acc + x)
iter.for_each(|x| process(x))
collect() Power
let v: Vec<_> = iter.collect();
let map: HashMap<_, _> = pairs.into_iter().collect();
let s: String = chars.collect();
let results: Result<Vec<_>, _> = items.iter().map(parse).collect();
Custom Iterator
struct Fibonacci { a: u64, b: u64 }
impl Fibonacci {
fn new() -> Self { Self { a: 0, b: 1 } }
}
impl Iterator for Fibonacci {
type Item = u64;
fn next(&mut self) -> Option<u64> {
let result = self.a;
let new_b = self.a + self.b;
self.a = self.b;
self.b = new_b;
Some(result)
}
}
let first_10: Vec<_> = Fibonacci::new().take(10).collect();
Performance
Iterator chains are zero-cost abstractions — they compile to the same assembly as hand-written loops. The compiler fuses adaptors into a single pass (no intermediate allocations).
let sum: i32 = v.iter().filter(|&&x| x > 0).sum();
let mut sum = 0;
for &x in &v { if x > 0 { sum += x; } }
Reference Map
references/closure-semantics.md — capture modes, Fn traits, move, higher-order functions
references/iterator-adaptors.md — all adaptors with examples
references/custom-iterators.md — implementing Iterator, IntoIterator, DoubleEndedIterator
Key References