Consider the following closure that returns an async block:
|entity: &mut Entity| async move {
entity.foo().await;
}
Is it possible to type erase and store this closure inside an enum, without specifying a generic type or lifetime on that enum? Consider the following MWE:
use std::future::Future;
struct Entity;
impl Entity {
async fn foo(&mut self) {}
}
fn main() {
erase_and_store(|entity: &mut Entity| async move {
entity.foo().await;
});
}
fn erase_and_store<'a, C, F>(closure: C) -> Task where
C: FnOnce(&'a mut Entity) -> F,
F: Future<Output = ()> + 'a {
Task::Variant(/* TODO convert closure to something that can be stored */)
}
enum Task {
Variant(/* TODO store closure */)
}
I have tried a couple different approaches but it seems that even if I put everything behind boxed trait objects, I can not stop this generic lifetime 'a
from leaking through to my Task
enum.
type AnyFuture<'a> = Box<dyn Future<Output = ()> + 'a>;
type AnyClosure<'a> = Box<dyn FnOnce(&'a mut Entity) -> AnyFuture<'a>>;
enum Task {
Variant(AnyClosure) // requires <'a>
}
What you want is a higher-ranked lifetime:
type AnyFuture<'a> = Pin<Box<dyn Future<Output = ()> + 'a>>;
type AnyClosure = Box<dyn for<'a> FnOnce(&'a mut Entity) -> AnyFuture<'a>>;
That can be elided:
type AnyFuture<'a> = Pin<Box<dyn Future<Output = ()> + 'a>>;
type AnyClosure = Box<dyn FnOnce(&mut Entity) -> AnyFuture<'_>>;