Oh, well, I’m saying that in case you were to say about sufficiently many API-design details that they must not change, then there might be no more way left of “making it work”. I.e., as I’ve clarified later, there’s no clear / canonical way to make it work, and how easy the problem is to solve depends entirely on more concrete considerations.
The thing… well, one of the things… that Rust’s borrow checker prevents is shared and mutable access to a value. So you can’t have two (usable/active) &mut …
references to the same value at the same time. (Or even one &mut …
and one &…
reference.)
Now, closures work by capturing local variables. E.g. the ||{ let _ = self.b(); return false; }
closure mentions self
in a fn (&self)
method call (for b()
), so it needs access to a &Self
reference; this reference is captured, i.e. it becomes part of the closure itself. Passing this closure into the self.a(…)
call, alongside the receiver self: &mut Self
means that there are two references to the same value passed as different arguments to a method call, so both values are active / in-use at the same time, yet at least one of them has mutable access.
Now, with impl FnMut(&mut Self) -> bool
, you can have the closure not capture self
, but receive it as an argument. The full change is
struct A { a: () }
impl A {
fn b(&self) -> () { self.a }
fn a(&mut self, mut pred: impl FnMut(&mut Self) -> bool) {
let _ = pred(self);
}
fn __(&mut self) {
self.a(|this|{ let _ = this.b(); return false; })
}
}
Now, there’s no more second reference to the same destination as self
in the closure passed to the self.a(…)
call as part of the closure. Inside of the implementation of fn a(…)
, you can then re-borrow self
to pass a short-lived reference to the closure to use while the call lasts. In many cases, this is not a terribly restrictive thing to do, but it does mean that you couldn’t to something like e.g.
let mut some_iterator = self.some_field.iter();
// do some things with `some_iterator`
/* call */ pred();
// do some more things with `some_iterator`
anymore, because that would now look like
let mut some_iterator = self.some_field.iter();
// do some things with `some_iterator`
/* call */ pred(self);
// do some more things with `some_iterator`
and result in a compiler error pointing out something along the lines of
error[E0502]: cannot borrow `self` as mutable because it is also borrowed as immutable
let mut some_iterator = self.some_field.iter();
---- immutable borrow occurs here
// do some things with `some_iterator`
/* call */ pred(self);
^^^^ mutable borrow occurs here
// do some more things with `some_iterator`
------------- immutable borrow later used here