Closures in Rust
Closures are anonymous functions that can capture their environment. They implement one of Fn, FnMut, or FnOnce depending on how they capture.
Syntax and inference
let add = |a: i32, b: i32| a + b; // types inferred
after_map(vec![1,2,3], |x| x * 2);
Capture and traits
Fn: captures by shared referenceFnMut: captures by mutable referenceFnOnce: captures by value (moves) and may be called once
fn call_twice<F: Fn(i32)>(f: F) { f(1); f(2); }
fn with_acc<F: FnMut(i32)>(mut f: F) { f(1); f(2); }
fn consume<F: FnOnce(String)>(f: F) { f("hi".into()); }
The compiler picks the least restrictive trait automatically.
move closures
Force capture by value; required for threads.
let v = vec![1,2,3];
std::thread::spawn(move || {
println!("{:?}", v);
}).join().unwrap();
Returning and storing closures
Use impl Trait or trait objects.
fn make_adder(x: i32) -> impl Fn(i32) -> i32 { move |y| x + y }
struct Handler<F: Fn(&str)> { cb: F }
let h = Handler { cb: |s| println!("{s}") };
let boxed: Box<dyn Fn(i32) -> i32> = Box::new(|n| n + 1);
Closures capturing references must outlive their uses; use move or adjust lifetimes when needed.
Using closures with iterators
let evens: Vec<_> = (0..10).filter(|n| n % 2 == 0).collect();
Tips
- Choose the simplest bound (
Fn/FnMut/FnOnce) that works for your API. - Prefer
movefor threads and to avoid borrowing pitfalls.