Your problem is the Fut: 'static
restriction. Your function async fn p(token: &str, message: &Message)
returns a future type that (implicitly) contains the function arguments, which are short-lived references, and hence Fut: 'static
is only met when you call this with &'static str
and &'static Message
arguments, too. However you’re after a general for<'b, 'c> Observer<(&'b str, &'c Message)>
, so that doesn’t work.
The 'static
bound comes about because you’re returning a Pin<Box<dyn Future<Output = ()>>>
which is a shorthand for Pin<Box<dyn Future<Output = ()> + 'static>>
. Let’s try to generalize to a Pin<Box<dyn Future<Output = ()> + 'a>>
of any lifetime. This isn’t quite so straightforward, because you still need to connect this lifetime to the inputs somehow.
Something like A1: 'a, A2: 'a
seems a good starting point, we can try it
impl<F, Fut, A1, A2> Observer<(A1, A2)> for F
where
F: Fn(A1, A2) -> Fut,
Fut: Future<Output = ()>,
{
fn call<'a>(&self, args: (A1, A2)) -> Pin<Box<dyn Future<Output = ()> + 'a>>
where
A1: 'a,
A2: 'a,
{
Box::pin(self(args.0, args.1))
}
}
error[E0195]: lifetime parameters or bounds on method `call` do not match the trait declaration
alright…
trait Observer<Args> {
fn call<'a>(&self, args: Args) -> Pin<Box<dyn Future<Output = ()> + 'a>>;
}
error[E0276]: impl has stricter requirements than trait
--> src/main.rs:17:13
|
7 | fn call<'a>(&self, args: Args) -> Pin<Box<dyn Future<Output = ()> + 'a>>;
| ------------------------------------------------------------------------- definition of `call` from trait
...
17 | A1: 'a,
| ^^ impl has extra requirement `A1: 'a`
...
18 | A2: 'a,
| ^^ impl has extra requirement `A2: 'a`
this should do it:
trait Observer<Args> {
fn call<'a>(&self, args: Args) -> Pin<Box<dyn Future<Output = ()> + 'a>>
where
Args: 'a;
}
actually, not quite:
error[E0309]: the parameter type `Fut` may not live long enough
--> src/main.rs:22:9
|
17 | fn call<'a>(&self, args: (A1, A2)) -> Pin<Box<dyn Future<Output = ()> + 'a>>
| -- the parameter type `Fut` must be valid for the lifetime `'a` as defined here...
...
22 | Box::pin(self(args.0, args.1))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `Fut` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound
|
20 | A2: 'a, Fut: 'a
| ~~~~~~~~~
now this seems unfortunate. As far as I can tell, the way this is declared, the compiler does not make the usual implications that lifetime bound from a trait can transfer to associated types. But we have a possible solution: just don’t introduce a type variable for it. We use a helper trait instead.
This pattern is already available in a general macro-generated collections of traits like this one that I've published in a crate. Let's copy the definition of that into our playground and use it:
impl<F, A1, A2> Observer<(A1, A2)> for F
where
F: AsyncFn2<A1, A2, Output = ()>,
{
fn call<'a>(&self, args: (A1, A2)) -> Pin<Box<dyn Future<Output = ()> + 'a>>
where
A1: 'a,
A2: 'a,
{
Box::pin(self(args.0, args.1))
}
}
error[E0309]: the associated type `<F as AsyncFn2<A1, A2>>::OutputFuture` may not live long enough
--> src/main.rs:61:9
|
56 | fn call<'a>(&self, args: (A1, A2)) -> Pin<Box<dyn Future<Output = ()> + 'a>>
| -- the associated type `<F as AsyncFn2<A1, A2>>::OutputFuture` must be valid for the lifetime `'a` as defined here...
...
61 | Box::pin(self(args.0, args.1))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `<F as AsyncFn2<A1, A2>>::OutputFuture` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound
|
59 | A2: 'a, <F as AsyncFn2<A1, A2>>::OutputFuture: 'a
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
aaaand… I forgot that this deduction of lifetime bounds for associated types also needs Self
restricted. Let’s add that
impl<F, A1, A2> Observer<(A1, A2)> for F
where
F: AsyncFn2<A1, A2, Output = ()>,
{
fn call<'a>(&self, args: (A1, A2)) -> Pin<Box<dyn Future<Output = ()> + 'a>>
where
Self: 'a,
A1: 'a,
A2: 'a,
{
Box::pin(self(args.0, args.1))
}
}
error[E0276]: impl has stricter requirements than trait
--> src/main.rs:58:15
|
47 | / fn call<'a>(&self, args: Args) -> Pin<Box<dyn Future<Output = ()> + 'a>>
48 | | where
49 | | Args: 'a;
| |_________________- definition of `call` from trait
...
58 | Self: 'a,
| ^^ impl has extra requirement `F: 'a`
and also to the trait definition:
trait Observer<Args> {
fn call<'a>(&self, args: Args) -> Pin<Box<dyn Future<Output = ()> + 'a>>
where
Self: 'a,
Args: 'a;
}
And now that all works! 
Note however that with current compiler limitations, you'll probably not be able to pass anything but literal async fn
items to this; notably closures don’t work in my experience, the compiler doesn’t get happy with the lifetimes there (for reasons that are more just arbitrary limitations of the current compiler than anything else).
Since you're turning this into Pin<Box<dyn Future …>
-returning functions anyways, you could consider accepting such a signature for message_create
already. This would mean users with functions that don't box their futures yet, the caller will need to write a small wrapping closure boxing the returned future themself; however it has the huge benefit of supporting closures at all. (You could of course also consider allowing both versions of the API