Hi all,
I am trying to create a registry of callback functions, where each has arguments extracted from a common Input
type. I have created a trait Args
for the possible argument types to encapsulate the extraction logic, the results of which can refer to parts of the input and so has a lifetime parameter. Similarly, the Output
type of these callbacks sometimes needs to return data from those arguments.
At the time we're building this registry, we don't yet have the input and so to create a list of the callbacks from Input
to Output
, I want to write something which wraps a body that takes Args
with one that extracts them from the input, something like:
fn wrap<A, B>(body: B) -> impl for<'a> Fn(&'a Input) -> &'a Output
where for<'a> A: Args<'a>, B: Fn(A) -> &'a Output () { ... }
But we cannot write the HRTB for both A and B like that. Based on other answers to similar questions, it sounded like a possible path to resolve is by pulling out a trait for the body, something like:
struct Input<'a> {
// ...
}
struct Output<'a> {
// ...
}
struct Args<'a>: Sized {
fn extract(c: Input<'a>) -> Self;
}
trait Body<'a> {
type Args: Args<'a>;
}
impl<'a, A, B> Body<'a> for B where B: Fn(A) -> Output<'a>, A: Args<'a> {
type Args = A;
}
fn wrap<A, B>(body: B) -> impl for<'a> Fn(Input<'a>) -> Output<'a>
where
for<'a> B: Body<'a> + Fn(A) -> Output<'a>,
{
move |c| body(<B as Body>::Args::extract(c))
}
// (trimmed for brevity)
But this doesn't work:
Compiling playground v0.0.1 (/playground)
error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types
--> src/lib.rs:27:36
|
27 | for<'a> B: Body<'a> + Fn(A) -> Output<'a>,
| ^^^^^^^^^^
error: aborting due to previous error
error: could not compile `playground`
To learn more, run the command again with --verbose.
I've tried a number of different variations here but either end up with an error like this, or an error that the implementation of body is not general enough (I think because the Body impl has to re-state the bounds and runs into the same problem). I've also tried having the Body trait expose some call
method itself, but either I have the same problems as above or I get errors about the Args type parameter being unconstrained despite appearing in the Fn type.
I'm pretty new to rust and feel like there is some fundamental thing I'm missing here about the goal or limitations I'm running up against; can anyone give suggestions about whether something like this is possible or pointers towards other code/discussions about something similar?