26
fn main() {
    let mut a = String::from("a");
    let closure = || {
        a.push_str("b");
    };

    closure();
}

This won't compile:

error[E0596]: cannot borrow immutable local variable `closure` as mutable
 --> src/main.rs:7:5
  |
3 |     let closure = || {
  |         ------- consider changing this to `mut closure`
...
7 |     closure();
  |     ^^^^^^^ cannot borrow mutably

If I return a in the closure without adding mut, it can be compiled:

fn main() {
    let mut a = String::from("a");
    let closure = || {
        a.push_str("b");
        a
    };

    closure();
}

This confuses me a lot. It seems like when I call closure(), closure will be borrowed if something is mutable inside it. Why won't it be borrowed when I return a?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Dajavu
  • 381
  • 4
  • 5

1 Answers1

28

There are 3 function traits in Rust: Fn, FnMut and FnOnce. Going backward:

  • FnOnce only guarantees that the value can be called once,
  • FnMut only guarantees that the value can be called if it is mutable,
  • Fn guarantees that the value can be called, multiple times, and without being mutable.

A closure will automatically implement those traits, depending on what it captures and how it uses it. By default, the compiler will pick the least restrictive trait; so favor Fn over FnMut and FnMut over FnOnce.

In your second case:

let mut a = String::from("a");
let closure = || {
    a.push_str("b");
    a
};

This closure requires being able to return a, which requires FnOnce. It moves a into the capture. If you tried to call your closure a second time, it would fail to compile. If you tried to access a, it would fail to compile too.

This is why FnOnce is a "last resort" implementation.

On the other hand your first case:

let mut a = String::from("a");
let closure = || {
    a.push_str("b");
};

At most requires a mutable reference to a, and therefore the capture occurs by mutable reference. Since it captures a mutable reference, the closure implements FnMut, and therefore can only be called if it is itself mutable.

If you remove mut, in front of a, the compiler will signal to you it needs to borrow a mutably.

The compiler does not require that closure itself be declared mutably until you attempt to call it; after all you could pass it by value to a function without calling it (yourself), in which case mut would be superfluous.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Regarding your latest paragraph. Would the function that receives the passed-by-value function mark the closure as `mut` in its signature? – Paul Razvan Berg Dec 07 '20 at 15:32
  • 3
    @PaulRazvanBerg: It may, but wouldn't need to. This is because if you have ownership of something, you can always rebind it to a mutable binding. That is `let a = ...; let mut a = a;`. – Matthieu M. Dec 07 '20 at 16:41
  • Oh, I see. Thanks for your prompt reply Sir! – Paul Razvan Berg Dec 07 '20 at 17:15
  • Closures capturing variables can not be coerced to Fn, FnMut or FnOnce, so it is not possible to provide them as paramters to a function as of Rust 1.68 – Dávid Tóth Apr 14 '23 at 03:46