Why in case of add function mut keyword is not mandatory, but in case of add_assign it's mandatory?
use std::ops::{Add, AddAssign};
let mut ssa = String::new();
let rr = move || { // no mut for rr is needed
ssa = ssa.add("ds");
println!("{}", ssa);
};
rr();
let mut ssa = String::new();
let mut rr = move || { // mut is mandatory
ssa.add_assign("ds");
println!("{}", ssa);
};
rr();
A closure desugars into an anonymous struct that implements the Fn traits (std::ops::{Fn, FnOnce, FnMut} as possible, see more in the docs. Your first closure implements only FnOnce. Your second implements FnMut, too).
To understand what's happening here, let's desugar the closures ourselves (note: correct desugaring requires nightly, since implementing the Fn* traits is unstable):
struct Closure1 {
ssa: String,
}
impl FnOnce<()> for Closure1 {
type Output = ();
extern "rust-call" fn call_once(mut self, (): ()) {
self.ssa = Add::add(self.ssa, "ds");
println!("{}", self.ssa);
}
}
let /*mut*/ ssa = String::new();
let rr = Closure1 { ssa };
FnOnce::call_once(rr, ());
struct Closure2 {
ssa: String,
}
impl FnOnce<()> for Closure2 {
type Output = ();
extern "rust-call" fn call_once(mut self, (): ()) {
FnMut::call_mut(&mut self, ())
}
}
impl FnMut<()> for Closure2 {
extern "rust-call" fn call_mut(&mut self, (): ()) {
AddAssign::add_assign(&mut self.ssa, "ds");
println!("{}", self.ssa);
}
}
let /*mut*/ ssa = String::new();
let mut rr = Closure2 { ssa };
FnMut::call_mut(&mut rr, ());
Playround.
What do we have here?
The first closure calls <String as Add>::add(). This method takes ownership of the String, and so it can only implement FnOnce, because it should move out of self.
On the other hand, the second closure calls <String as AddAssing>::add_assign(). This method takes &mut self, and so this closure can implement FnMut, too, as it only needs to take a mutable reference to ssa. If weren't using move, the closure would not own ssa even and just hold a mutable reference to it; but since you are using move you force the compiler to move ssa into the closure.
When calling the closures, the first closure implements FnOnce, and thus, when called, it is called with FnOnce::call_once() that moves the closure. If you try to call it again, it'll fail with "use of moved value".
But the second closure is called with FnMut::call_mut() (because the compiler always picks "weakest" trait to call with), which takes &mut self. That means it does not move the closure - thus you can call it twice, but on the other hand, you need to take a mutable reference to the closure - so it needs to be marked mut!
(If you're wondering why the first closure requires ssa to be marked mut while it moves it, indeed, notice that in the desugaring I commented out the mut. However, the compiler knows about closures and considers an assignment in a closure to require mut access, since it'd be very confusing if it did not.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With