There is no fundamental reason it has to work this way for safety or anything. But there is no other place to mark the capture itself as mutable, so that's how the syntax was designed.
The mut in let mut y mark the closure (and so its fields) as mutable. I think forcing x to be mutable is misleading and inconsistent with the rest of the language.
But what if you want closure_state_x to be mutable but closure_state_y to be immutable? Currently there is no way to declare a change of mutability upon capture so its just inhereted from the declaration.
If you really don't want the outer x to be mutable, you can rebind it for the closure:
fn main() {
let x = 2;
let mut y = {
let mut x = x;
move || {
x += 1;
x
}
};
let (a, b) = (y(), y());
println!("{a} {b} {x}");
}
That technique is also useful when you want to mix some captures moved by value with others by reference -- then you can create explicit references and move those instead.
This rebind technique is also very useful when using tokio::spawn(async move { t }), the inner Future cannot carry non-static lifetimes, so you need to move everything, and therefore .clone() everything.
There are two ways I usually do this:
let name = self.name.clone();
let rx = self.receiver.clone();
tokio::spawn(async move {
// Use `name` and `rx`
});
and like this (similar to what cuviper showed, cloned in the inner scope):
tokio::spawn({
let name = self.name.clone();
let rx = self.receiver.clone();
async move {
// Use `name` and `rx`
}
});
TLDR: if you see tokio:spawn (or Fut: 'static) prepare to move, and if you see move, prepare to rebind! :v