4

Why do I need to make this closure variable mutable? The closure isn't returning anything, therefore nothing is being stored in the closure variable. This closure is only capturing a value from the environment and incrementing it.

fn main() {
    let mut x = 1;
    let mut y = || x = x + 1;
    y();
    println!("{}", x);
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Muhammad Naufil
  • 2,420
  • 2
  • 17
  • 48
  • 2
    It looks like your question might be answered by the answers of [When does a closure implement Fn, FnMut and FnOnce?](https://stackoverflow.com/q/30177395/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Dec 10 '19 at 15:33

2 Answers2

11

Essentially, this is a consequence of one of Rust's soundness rules – you can't modify anything through a shared reference to a mutable reference. All the references in your chain of references need to be mutable for this to be possible.

Let's translate the closure in your example into roughly equivalent code using a struct:

struct Closure<'a> {
    x: &'a mut i32,
}

impl Closure<'_> {
    fn call(&mut self) {
        *self.x += 1;
    }
}

The closure captures x by mutable reference – it has to to be able to modify it. When the closure is called, it receives self by mutable reference – it has to to be able to mutate x through the self pointer.

Now if you want to call this closure, you need to make it mutable as well:

let mut x = 1;
let mut y = Closure { x: &mut x };
y.call();

If you don't make y mutable, you can't pass a mutable reference to y to the call() method.

The reason for the soundness rule mentioned at the beginning is easy to see: If you could mutate through a shared reference to a mutable reference, it would be easy to write code with multiple players holding mutable references to the same memory location, which violates one of the fundamental rules about Rust references.

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
-1

You have the situation exactly backwards. Returning a value does not require storing anything in the closure. Capturing, on the other hand, does require storing something in the closure. In your case, y needs to store a reference to x. And since that reference is mutable, the closure needs to be mutable.

Here you can see the difference in size between a closure that returns something (but captures nothing) and a closure that captures, and a closure that captures even more things.

fn main() {
    let mut a = 1;
    let mut b = 1;
    let mut c = 1;

    let small = || {
        return 10;
    };

    let mut bigger = || {
        a = a + 1;
    };

    let mut biggest = || {
        b = b + 1;
        c = c + 1;
    };

    small();
    bigger();
    biggest();

    println!("{}, {}, {}",
        std::mem::size_of_val(&small),
        std::mem::size_of_val(&bigger),
        std::mem::size_of_val(&biggest)
    );
}

Output:

0, 8, 16

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f48399e830f6a6e597338114ea30a409

And for completeness, here is a closure that captures a value, but doesn't mutate it. You can see that it doesn't need to be mutable.

fn main() {
    let x = 1;
    let y = || x;
    println!("{}, {}", y(), std::mem::size_of_val(&y));
}
Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
  • 2
    your answer is not clear, I don't see why you talk about storing thing, this closure auto implement fnmut because its capture a mutable reference and to call it you need to borrow it as mutable https://doc.rust-lang.org/std/ops/trait.FnMut.html#tymethod.call_mut but actually nothing prevent this function to implement FnOnce like this https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1e96ed8833737d1feb696fe4b35dab4c. – Stargateur Dec 10 '19 at 15:29
  • 1
    @Stargateur: I talked about storing things, because that seems to be what the OP was confused about. – Benjamin Lindley Dec 10 '19 at 15:35
  • 1
    *since that reference is mutable, the closure needs to be mutable* — that's not quite accurate (or it's at least imprecise). [Here's an example](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ef7499d0c31615405d11f49fd113a9c7) with a closure that captures a mutable reference but doesn't need to be mutable itself. – Shepmaster Dec 10 '19 at 16:09