Closures power iterators, callbacks, threads and async code, and their capture rules follow directly from ownership.
A closure is an anonymous function that can capture variables from the scope where it is defined: let add = |a, b| a + b; or with a block body |x: i32| -> i32 { x * factor }. Parameter and return types are usually inferred from the first use.
Closures capture in the least demanding way their body allows: by shared reference if they only read a variable, by mutable reference if they modify it, or by value if they move it out. Borrowing rules apply to those captures, so a closure holding &mut count blocks other uses of count while it is alive.
The capture mode decides which Fn trait the closure implements:
FnOnce: can be called at least once; it may consume captured values. Every closure implements it.FnMut: can be called repeatedly and may mutate captures.Fn: can be called repeatedly through a shared reference; it only reads captures.
APIs take the most permissive bound that works for them: Option::map takes FnOnce because it calls once, Iterator::for_each takes FnMut, and things called concurrently or many times through & take Fn.
The `move` keyword forces captures by value, even if the body only reads them. You need it when the closure outlives the current scope: returning a closure, spawning a thread, or passing it into async tasks. Moving a Copy value copies it; to move a clone and keep the original, clone into a new variable first.
To accept a closure, use generics (fn apply<F: Fn(i32) -> i32>(f: F)), and to return one, -> impl Fn(i32) -> i32. When you need to store closures of different types together, box them: Vec<Box<dyn Fn(i32) -> i32>>. Plain fn items also implement all three traits, so you can pass a function name wherever a closure is expected.
Example
fn apply_twice<F: FnMut()>(mut f: F) { f(); f(); }
fn make_multiplier(k: i32) -> impl Fn(i32) -> i32 {
move |x| x * k // `move`: the closure owns its copy of k
}
fn main() {
let mut count = 0;
apply_twice(|| count += 1); // captures &mut count -> FnMut
println!("count = {count}"); // count = 2
let triple = make_multiplier(3);
println!("{}", triple(7)); // 21
let name = String::from("ferris");
let consume = move || name; // moves name out when called -> FnOnce only
let back = consume();
// consume(); // error: closure cannot be called twice
println!("{back}");
let ops: Vec<Box<dyn Fn(i32) -> i32>> = vec![Box::new(|x| x + 1), Box::new(triple)];
println!("{:?}", ops.iter().map(|f| f(10)).collect::<Vec<_>>()); // [11, 30]
}Remember
- Closures capture by reference, mutable reference or value, as needed by the body
Fn⊂FnMut⊂FnOnce: callers ask for the weakest bound they can- Use
movewhen the closure must outlive the scope that created it
Watch out for
- Forgetting
moveon a closure passed tothread::spawnand getting a 'may outlive borrowed value' error.