I was making the executor/reactor while discovered this a lifetime problem. It is not related to async/Future and can be reproduced without async sugar.
use std::future::Future;
struct Runtime;
fn start_with_runtime<C, F>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> F,
F: Future
{
let rt = Runtime;
let _future = closure(&rt);
// block_on(future);
}
async fn async_main(_rt: &Runtime) {
// I can use _rt to do async stuff here
}
fn main() {
start_with_runtime(|rt| { async_main(rt) });
}
I would like to the start_with_runtime()
to run the future and provide the async Runtime reference as parameter.
It does not compile:
error: lifetime may not live long enough
--> src/main.rs:17:31
|
17 | start_with_runtime(|rt| { async_main(rt) });
| --- ^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure is impl std::future::Future
| has type `&'1 Runtime`
I think that this problem seems to be because how rust infers lifetimes for closures:
https://github.com/rust-lang/rust/issues/58052 :
fn main() {
let f = |x: &i32| x;
let i = &3;
let j = f(i);
}
Does not compiles either:
error: lifetime may not live long enough
--> src/main.rs:2:23
|
2 | let f = |x: &i32| x;
| - - ^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure is &'2 i32
| let's call the lifetime of this reference `'1`
Looks like my closure signature inferred as |&'a Runtime| -> impl Future + 'b
and thus the lifetime error. I feel that given correct expected signature for closure would help, but how do I provide the correct signature in start_with_runtime
?
fn start_with_runtime<C>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> (impl Future + 'a),
Does not work because impl Trait
is not allowed here.
fn start_with_runtime<C,F>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> F,
F: Future + 'a
Does not work as well because 'a
is not known outside of HRTB expression.
It works if I know the type:
struct MyType<'a> {
_rt: &'a Runtime
}
fn start_with_runtime<C>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> MyType<'a>,
This is kind of sad when you've thought through all the lifetimes but language does not provide a way to express this. Perhaps there is a trick in rust to make this work?
start_with_runtime
to main? Because that should work, without any explicit lifetimes. – Tautogstart_with_runtime
supposed to be in a crate and used by the apps (e.g. hiding Runtime construction from apps). This is kind of backup plan that app canlet rt = Runtime::new(); rt.run(|rt| my_async_fn(rt));
– EurasianRc<Runtime>
instead of a reference to the runtime work for you? – DarbRc<>
would work, but this is overhead and does not like a right ownership model to me. – Eurasian