Rust How Closures Work
A closure is a small struct that the compiler builds. The struct holds the captured values as fields.
A closure is a tiny struct
When you write this closure, picture a struct holding one field. The field is a shared reference to factor. The struct also has one method that runs the body. Rust invents the struct for you. Its name is unprintable. That is why closure types never appear in your code:
let factor = 3;
let times = |n: i32| n * factor;
println!("{}", times(7));Changing a capture takes &mut self
A closure that changes a captured value is called through &mut self, like a method that changes its struct. That is why the closure variable itself must be declared mut:
let mut score = 10;
let mut add = || { score += 5; score };
println!("{}", add());
println!("{}", add());move decides the capture, not the trait
This closure captures by move, yet it only reads what it took — so it is a full Fn closure. The helper twice demands F: Fn() and calls the closure through a shared reference, twice:
let word = String::from("hi");
let greeter = move || println!("{word}");
twice(&greeter);Calling an FnOnce closure uses it up
A closure whose body hands a capture out can be called only once, because the call takes self. The second call here is error E0382, use of moved value:
let gift = String::from("a book");
let give = move || gift;
println!("{}", give());
println!("{}", give()); // error E0382: use of moved valueClosures inherit traits from their captures
Copy, Clone, Send and Sync are worked out from the little struct's fields, as if derived. This closure captured only an i32, so the closure itself is Copy — copying it gives two working closures:
let base = 40;
let add_base = |n: i32| n + base;
let copy_of_it = add_base;
println!("{} {}", add_base(1), copy_of_it(2));Example
fn call_twice<F>(f: &F) -> String
where
F: Fn() -> String,
{
format!("{} / {}", f(), f())
}
fn main() {
// Reads its capture: Fn — callable through a shared reference.
let word = String::from("rust");
let reader = || format!("dear {word}");
println!("{}", call_twice(&reader));
// Changes its capture: FnMut — the binding must be mut.
let mut ticks = 0;
let mut ticker = || {
ticks += 1;
ticks
};
ticker();
ticker();
println!("ticks = {}", ticker());
// Hands its capture out: FnOnce — the call uses the closure up.
let gift = String::from("a book");
let give = move || gift;
println!("gift = {}", give());
}Key points
- call_once takes self, call_mut takes &mut self, call takes &self — the receiver is the whole ladder
- A closure earns its traits from its body.
- Moving a capture out caps it at FnOnce.
- Changing a capture caps it at FnMut.
- Only reading earns all three.
- How a value is captured never decides the traits.
- Borrowed or moved, it does not matter.
- What the body does with the value decides.
- Use this to read errors.
- E0596 (cannot borrow as mutable) means a call needed &mut self.
- E0382 right after a call means the call took self.
In this example, three closures show the three receivers at work. reader only reads word. So call_twice can borrow &reader. It calls it twice through a shared reference: dear rust / dear rust. ticker changes ticks, so ticker itself must be mut, and its third call prints ticks = 3. give hands its String out of the closure. So its one call consumes the whole closure. It prints gift = a book. Same bars-and-body syntax each time — three different little structs underneath.
Note
You cannot write impl Fn for your own types.
The call traits are implemented by the compiler, for closures and function pointers only.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
A move closure takes ownership of a String, and its body only reads that String when called. Which traits does it implement?