Sort of . You can have trait objects via
&FnOnce too, although it’s just as useless here because you can’t move out of a borrow. The issue also isn’t strictly about moving it around.
You can probably just leave this bit out - now that I think about it, I don’t think it’s adding any extra value.
Here’s how I’d summarize the issue:
- You want to store different FnOnce types in a homogeneous container (
Vec<T>). You want the container to own these closures.
- You can’t store different closures in the Vec because each closure instance is a unique type.
- For that reason, you need to employ type erasure - aka trait objects in Rust.
- A (uniquely) owned trait object is done via Box - eg
At this point, you’re golden as far as storage (and moving things around) is concerned. The problem is in calling the closure:
- FnOnce, since it can only be called once (hence the name) takes
self by value and consumes itself.
Box<FnOnce()>, to call the inner closure, needs to have it by value. As such, the enclosed closure has to be “pulled out”. This can’t happen because traits, on their own, are unsized - the box itself is adding size information by virtue of storing a fat pointer. So it’s a catch 22.
- Using FnBox works because the call works off
self: Box<Self>, and thus doesn’t require pulling the innards out - the call goes through the box, and consumes the box in the process.
I’ll note that the fact
Box<FnOnce()> isn’t callable is a language wart, that may one day be eradicated.