Yeah, those bounds mean mostly the same thing, except
- the
for<'a> FnMut<(&'a TreeItem,)>
version is unstable, and
- the output type of the first is
()
while the output type of the second could be any Sized
type
so, bad diagnostics.
(for<'a> FnMut(&'a TreeItem)
is stable, and means the same as FnMut(&TreeItem)
).
Some elaboration for anyone in the audience:
First, note that a F: FnMut(..)
closure is called by taking a &mut _
to the closure -- the FnMut
method that corresponds to calling the closure takes &mut self
. So if you take such a closure by value, for example, you have to making the binding mutable.
Next, note that without a Copy
bound, passing a F: FnMut(..)
closure will move it -- you'll give away ownership of the closure. So you won't be able to call it any more. But, copying such a closure would generally defeat the point of FnMut
-- any state the closure was keeping track of would be cloned too, instead of maintaining that state across all the calls you make. (So adding a Copy
bound isn't really what you want with FnMut
either.)
The moving/ownership is the source of the original error: you can't pass it to multiple children by value.
&mut _
don't implement Copy
either, but you can reborrow them. And this is the key to one of the solutions: just take a &mut F
, which you can use to call the closure (FnMut::call_mut(..)
), and you can reborrow the &mut F
to make the recursive call.
Splitting the method up into a public method that takes a closure by value, and another that takes a &mut _
, is primarily an ergonomic improvement for callers of the method, so they don't have to take a &mut
to their closures themselves.
Finally, however, let's look at a midway approach that fails:
// Pretty much the same as:
// fn apply<F: FnMut(&TreeItem)>(&self, mut func: F) { .. }
fn apply(&self, mut func: impl FnMut(&TreeItem)) {
func(self);
for child in &self.children {
// Don't give away ownership, so this should work ... right?
child.apply(&mut func)
}
}
The problem is that how far the recursive call goes is a dynamic property, whereas Rust needs to know every possible type parameter F
that apply
is called with ac compile time, because that's how generic type parameters work: every distinct type gets its own monomorphized function. So it goes something like...
- Monomorphize
apply
with F = F0
, then...
- Monomorphize
apply
with F = &mut F0
, then...
- Monomorphize
apply
with F = &mut &mut F0
, then...
- Monomorphize
apply
with F = &mut &mut &mut F0
, then...
and eventually reaches a recursion limit.
Whereas with the split approach it goes something like:
- Monomorphize
apply
with F = F0
(parameter is F
)
- Monomorphize
apply_inner
with F = F0
(parameter is &mut F
)
- Calls
apply_inner
with F = F0
again, no need to monomorphize anything else
Using &mut dyn FnMut(..)
would also avoid the infinite type recursion/monomorphization, without splitting up the method. However, it's also a bad idea! Unless you really luck out with devirtualization, you'll be adding layer of indirection every time, so at the bottom you might have a
&mut
(&mut
(&mut
F0
as &mut dyn FnMut(..)
)
as &mut dyn FnMut(..)
)
as &mut dyn FnMut(..)
...
as deep as your tree is.