I am implementing a library. I'd like the library not to depend on a specific asynchronous runtime.
I have a partially-sealed MySpawn
trait with an asynchronous spawn
method:
pub trait MySpawn {
async fn spawn<F, Fut>(&mut self, f: F) -> u32
where
F: FnOnce() -> Fut,
Fut: Future<Output = ()>;
}
The implementations of the spawn
method need to:
- mutate
self
; call asynchronous functions; - call
f
and obtain aFut
future; - spawn a task onto a runtime; the task awaits the
Fut
future and does other operations; - mutate
self
; call asynchronous functions; - return a value (suppose
u32
).
The trait is partially sealed so that the user can only decide how to perform point 3.
Is is possible to modify the trait so that point 3 is runtime agnostic?
The first attempted solution I thought is to return the task and ask the caller to spawn it, but this won't work because the method would still need to do 4 and 5.
The second attempted solution I thought is to modify the method adding an S
closure that takes the task as parameter; the implementation of S
would then contain a tokio::spawn
call (or a call to some other runtime) with the task as argument. This won't work, because we can't name the type of the task.
Playground of the second attempt
Of course, there is the inconvenience that the user could forget to call the runtime in the closure.
We can fixed this by requiring the closure to return a type that is not directly constructible by the user but that can be only obtained by the Fut
, like this.