Why must closure variable be mut

Why does inner need to be mut in this example?
I never modify the inner variable which is assigned a closure.
Is it because any closure that modifies a variable in its environment must be mut?

fn main() {
    let mut a = String::new();
    let mut inner = | | {
        let b = String::from("test");
        a = b; // Moves ownership from b to a.
    };
    inner();
    println!("{}", a);
}

(Playground)

Output:

test

Errors:

   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 0.85s
     Running `target/debug/playground`

There's no error in the compiler output you posted, perhaps you meant to remove the mut first?

Anyway, yes, you have the right idea. See the signature for the call_mut method of FnMut, which is implemented by closures that modify their environment.

This post by @RustyYato may be of interest.

2 Likes

Since the closure doesn't move a into itself, it's capturing a mutable reference in order to perform a = b assignment. You can imagine the compiler generates a closure struct similar in spirit to:

struct Closure<'a>(&'a mut String); // captured `a` mutable reference

impl FnMut<()> for Closure<'_> {
     fn call_mut(&mut self, _: ()) {
         let b = String::from("test");
         *self.0 = b;
     }
}
3 Likes

To add to the previous commenters, here’s how the desugaring of the closure call works:

In the code

fn main() {
    let mut a = String::new();
    let mut inner = || {
        let b = String::from("test");
        a = b;
    };
    inner();
    println!("{}", a);
}

the compiler first deduces that the closure mutates a, so it includes a mutable reference to a in the closure. (This will e.g. mean that you cannot access a anymore until after the last call to inner.) Then it also sees that the closure does not move out of any of it’s captured variable. This means that it can implement FnMut but it cannot implement Fn.

The call to inner() thus gets desugared using the FnMut interface. You can think of the desugaring of such a call to use Fn interface whenever possible. (Not possible for this closure.) Then, as a fallback use FnMut interface (works works for this closure). And if the closure had not implemented FnMut it would’ve fallen back to using the FnOnce interface. In this case this means that we get

// needs nightly for explicitly mentioning
// the `Fn*` traits
#![feature(fn_traits)]
fn main() {
    let mut a = String::new();
    let mut inner = || {
        let b = String::from("test");
        a = b;
    };
    inner.call_mut(());
    println!("{}", a);
}

(playground)

As the previous commenters have shown, call_mut takes a &mut self argument, hence this desugars further to

// needs nightly for explicitly mentioning
// the `Fn*` traits
#![feature(fn_traits)]
fn main() {
    let mut a = String::new();
    let mut inner = || {
        let b = String::from("test");
        a = b;
    };
    FnMut::call_mut(&mut inner, ());
    println!("{}", a);
}

(playground)

This &mut inner reference being created is the reason why we need the mut when declaring inner.

5 Likes