I'm trying to implement a function that takes an async closure, but I don't know how to tell the compiler that the future resulting from the closure will be resolved before the closure argument from the enclosing function goes out of scope. Any help appreciated!
The input lifetime of the borrow over Foo is local to the callee,
Thus, a higher-order signature is needed: impl for<'local> Fn(&'local …)…
But when writing an async closure such as:
|foo: &'_ mut Foo| async move { … }
the value returned by that closure is the type of the async move { … } future, which captures foo, and thus, whose type is infected with the lifetime of foo.
That is, to correctly express the bounds of the closure, you'd need to write:
You have, however, used a fixed generic parameter Fut to represent the return type of that closure, that is you expected to have:
Fut = impl 'local + Future<…>
but then, what is 'local there? And there is no valid answer there: 'local doesn't make sense for Fut since, as a fixed generic parameter, its type is defined before the higher-order / universal quantification for<'local> ever gets introduced.
Hence the error.
The solution
is then to express that nested impl bound. Alas, this is not something that can be done on stable Rust, and even if it were, there would be the issue that the bound on the return type (that of being a Future<Output = ()>) usually leads to weird type-checker / trait solver errors, due to limitations in the compiler (lazy normalization bugs, to be exact).
For async fnonly (not for async closures!), using a helper trait such as:
trait MyAsyncFn<'foo> {
type Fut : Future<Output = ()>;
fn call (
self: &'_ Self, // Fn
_: &'foo mut Foo,
) -> Self::Fut;
}
impl<'foo, Fut : Future<Output = ()>, F : Fn(&'foo mut Foo) -> Fut>
MyAsyncFn<'foo>
for
F
{
type Fut = Fut;
fn call (
self: &'_ Self, // Fn
foo: &'foo mut Foo,
) -> Fut
{
self(foo)
}
}
(And then using impl for<'local> MyAsyncFn<'local>, and using f.call(&mut foo).await to call it)
does work: Playground. But for || async move { … } closures, this approach still runs into issues with the "async closure" definition not being imbued with the right higher-order signature. Maybe in the future with proper async move || { … } this solution would work .
Hence, the only reliable workaround for those bugs with |…| async move { … } closures (and, incidentally, around the nightly requirement): to replace
-> impl '_ + Future<…>
with
-> dyn '_ + Future<…>
And the latter requires pointer indirection, leading to: