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

  1. The variable storing the closure itself must be declared let mut if you call it directly, because calling a closure that mutates its captured environment requires &mut self (FnMut::call_mut).
  2. While the closure's &mut T borrow is active (between the closure's creation and its final call), the outer scope cannot read or mutate the captured variable directly, because &mut T is 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