Closures
Capture by Mutable Reference
When a closure modifies a variable from its enclosing scope (such as incrementing a counter or pushing to a Vec<T>) without moving it out, the compiler captures that variable by mutable reference (&mut T).
How Mutable Reference Capture Works
- The variable storing the closure itself must be declared
let mutif you call it directly, because calling a closure that mutates its captured environment requires&mut self(FnMut::call_mut). - While the closure's
&mut Tborrow is active (between the closure's creation and its final call), the outer scope cannot read or mutate the captured variable directly, because&mut Tis an exclusive borrow.
fn main() {
let mut log = Vec::new();
// Captures `&mut log`; binding `record` is declared `mut` so we can call it:
let mut record = |event: &str| {
log.push(event.to_uppercase());
};
record("connect");
record("authenticate"); // Final use of `record` — `&mut log` borrow ends here
// `log` can be read again now that `record` is no longer used:
println!("Recorded events: {log:?}");
}
Recorded events: ["CONNECT", "AUTHENTICATE"]
Exclusive Borrow Restriction While Closure Is Live (E0502)
Because the closure holds an exclusive &mut borrow of the captured variable from its definition until its last invocation, reading the variable in the outer scope before the final call to the closure fails with E0502:
// ❌ does not compile
fn main() {
let mut count = 0;
let mut increment = || count += 1;
increment();
println!("Intermediate count: {count}"); // Error: cannot borrow `count` as immutable
increment();
}
error[E0502]: cannot borrow `count` as immutable because it is also borrowed as mutable
--> src/main.rs:8:35
|
5 | let mut increment = || count += 1;
| -- ----- first borrow occurs due to use of `count` in closure
| |
| mutable borrow occurs here
6 |
7 | increment();
8 | println!("Intermediate count: {count}");
| ^^^^^^^ immutable borrow occurs here
9 | increment();
| --------- mutable borrow later used here
|
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
Passing a Mutating Closure to another Function without let mut
If you pass a mutating closure directly by value to a higher-order function (or bind it without calling it directly in that variable), the receiving parameter declares mut f: F, so the caller does not need let mut:
fn run_three_times(mut action: impl FnMut(usize)) {
for step in 1..=3 {
action(step);
}
}
fn main() {
let mut total = 0;
run_three_times(|step| total += step * 10);
println!("Total: {total}");
}
Total: 60