I did some research and discovered that Rust tries to borrow the closure struct member before I call it with (). Can anyone explain why Rust is trying to borrow self.closure? What is the logic behind this? This confused me quite a lot.
Moreover the type of reference is related to what kind of Fn... trait the closure implements. In this particular case, if I change FnMut to Fn, no errors occur, because we can have multiple shared references to self (including self.closure), but we cannot create unique reference to self.closure through a regular reference to it.
pub struct S<F> {
closure: F,
}
impl<F: FnMut()> S<F> {
pub fn test(&self) {
(self.closure)(); // <-- error
}
}
fn main() {
let s = S { closure: || () };
s.test();
let s_ref = &s;
(s_ref.closure)(); // <-- no error
}
(self.closure)();
^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
help: consider changing this to be a mutable reference
pub fn test(&mut self) {
~~~~~~~~~
When you create a closure, it's like defining a new (anonymous) struct that contains any captures the closures may have as fields. The compiler also automatically implements various traits for that struct, most pertinently, one or more of the Fn traits.
As you can see from the signature in the trait, calling a FnMut requires a &mut self to the implementor -- to the closure. Why? Because the closure is allowed to mutate its captures, and just like you need a &mut self to mutate your[1] fields, a closure needs a &mut self to mutate its[2] captures.
If you don't want to require &mut, use a Fn (or FnOnce, when applicable) bound instead. (That's what's going on with the version in main that compiles.)
This is a great explanation. Thanks a lot! Could you elaborate on why this works in the main?
Here is a straightforward analogy that does not compile with an error: cannot borrow *x_ref as mutable, as it is behind a & reference. Please point me where I am going wrong here.
fn main() {
let mut x = 0;
let x_ref = &x;
let x_ref_mut = &mut *x_ref; // <-- error
}
Does this make it any more clear? The "closure" (i32) implements MyFn and MyFnMut. But in test, you can only utilize MyFnMut, because that's the bound you mentioned on the generic F. In main, the concrete type is known, and all of it's trait implementations -- including MyFn -- can be utilized.
I'm not sure how your assignment example relates, so I can't comment on that.
Yes. Great! You have successfully unraveled what exactly I understand wrong, despite my inappropriate example. I've corrected it to demonstrate what I mean, but no further explanation is needed.
In a word: I didn't take into account that the bound only applies to the closure in the the test method, but not to the closure in general.