error: lifetime may not live long enough
error[E0310]: the parameter type `Fut` may not live long enough
What i already tried:
Adding lifetime annotations to From trait - makes everything worse (after adding this i get error: "the type parameter Fut is not constrained by the impl trait" what doesn't make any sens, because i only added lifetime annotation)
Making function that explicitly marks both lifetimes (of argument to closure and future) the same (fn assume_same_lifetime in example) - doesn't help
Changing future lifetime to 'static - this enforces me to use 'static on argument to closure what i'm trying to avoid
async move can't "undo" the fact that arg: &T is a temporary reference that may be destroyed immediately after the closure has been called, so your Future is not'static.
In your other implementations you're passing through the 'a that keeps the &'a T safe.
Also, definition of generic functions returning futures is kinda broken in Rust, because the where clause that defines the Fn closure type is separate from where clause that defines the Fut: Future, but they have to specify the same lifetime.
Unfortunately this doesn't solve problem with lifetimes, even after explicitly adding them, error still occurs and is somewhat ambiguous.
I had super trait item shadowing problems, because of the rust edition used in async_fn_traits crate.Therefore just copied trait and impl block to my code.
Related previous thread. Something that bites a lot around this topic is that the compiler is horrible at inferring higher-ranked, borrowing closures -- closures that take any lifetime as an input and pass that on to their output. Sometimes you can use the "funnel" trick to annotate your way around it...
// Identity function for the type of closures we want
fn funnel<T, C: Closure>(c: C) -> C
where
C: FnOnce(&T) -> Foo<'_>
{ c }
...but futures are unnameable so this doesn't always help, as explored in that other thread. In particular...
...this requires dealing with the unnameable futures before they are type erased. I don't know of anyway to fix Rust's poor inference so that this will work how you wish:
However, your new method itself can work with the funneling approach, since the desired return type is nameable due to type erasure at that point.
fn new_funnel<T, C>(c: C) -> C
where
C: 'static + Send + for<'a> FnOnce(&'a T) -> BoxFuture<'a, ()>,
{ c }
// N.b. I also adjusted the bounds from your latest playground as
// you had bounds for a single lifetime, but you need higher-ranked
// bounds for `BoxedAsyncFn<T>`
impl<T: 'static + Sync> AsyncFn<T> {
pub fn new<C>(value: C) -> Self
where
C: 'static + Send + for<'a> AsyncFnOnce1<&'a T, AsyncOutput = (), OutputFuture: Send>,
{
Self {
0: Box::new(new_funnel(move |arg: &T| Box::pin(value(arg)) ))
}
}
}
That being said, I don't think it really helps the overall design -- you've just pushed the inference problems onto callers of new. And if their only fix is type erasure too, you'll probably end up double-boxing the returned future type.
So I suspect it's still best for now that the pinning and boxing happens in the original closure.
Thank you for the link! There is already open issue in rust-lang github: #58052 that would probably fix this. Additionally i stumbled upon post that describes PR that solves this in nightly.
This is the first problem with a coroutine-closure, which returns a coroutine that is allowed to borrow from the closure's upvars, since there's no way to link the input lifetime (of the closure borrow) with the output lifetimes (in the coroutine's upvars).
Which explains why it can't work on stable, but with help of feature async_closure everything just works.