That’s a really good question. It solves my particular problem, so I can only really speak about what my problem was.
I won’t speak in specifics, since we’re not ready to announce our project yet, but I’ve come up with the following contrived example which has a request/result scenario with support for an “around” hook:
Apologies for the complexity of this example. In spite of my efforts to simplify it, this structure of traits is necessary to demonstrate the problem I ran into, particularly in that the
AroundHook can’t be made into a trait object because
call takes a closure.
In this example where we have only one
Dispatcher, the ownership is straightforward because we can store the hook where it’s going to be used. There are a few complicating factors as this grows in scope:
Registry will have multiple
Dispatcher values, and will need to determine which to use based on some field(s) of the
- We want to define the hooks only once, and potentially use them in many places, so our options are either to clone them or borrow them
- If we opt to clone hooks, duplicating
AroundHook values for every dispatcher requires a lot of
clone() calls as the
Registry grows, but perhaps worse imposes a need for the
AroundHook to be
Clone. Not every type can be
Clone, so that’s too limiting
- If we opt to store a reference instead, we run into the limitation that
AroundHook can’t be made into a trait object, because of the generic function which takes a closure, so we can only store a reference to the concrete type.
I couldn’t get the ownership to work correctly when using references. One example:
BorrowBag solves this by allowing a
BorrowBag<V> owning all the hooks to be stored in
Registry, and passed through the
dispatch call. The
DispatcherImpl struct can store as many
Handle<T, _> values as it needs to describe all the hooks it will use, and then during
dispatch can use those handles to borrow the hook values and dispatch.