I'm attempting to pass an async closure between threads in a tokio
based application, however I am running into a lifetime error I don't understand when trying to make this work.
I've created a minimal example of the issue in this playground:
use tokio; // 1.32.0
#[tokio::main]
async fn main() {
let (send, recv) = tokio::sync::oneshot::channel();
let val: u32 = 0;
let func = move |foo: &u32| async move { val + foo };
let _ = send.send(func);
let fut = async move {
let f = recv.await.expect("receiver should not fail");
let foo: u32 = 1;
let res = f(&foo).await;
println!("Result: {}", res);
assert_eq!(res, 1);
};
tokio::spawn(fut).await.expect("thread should succeed");
}
Which yields this error when compiling:
Compiling playground v0.0.1 (/playground)
error: lifetime may not live long enough
--> src/main.rs:10:33
|
10 | let func = move |foo: &u32| async move { val + foo };
| - - ^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure `[async block@src/main.rs:10:33: 10:57]` contains a lifetime `'2`
| let's call the lifetime of this reference `'1`
error: could not compile `playground` (bin "playground") due to previous error
If I understand correctly, the error is telling me that the lifetime of the argument to the closure (foo
) must outlive the return value of the closure. Why is this the case, why is it not satisfied, and how can I modify the code to avoid this error?
I believe this is a quirk of closure inference. Unfortunately, there is no good way to prevent it.
If boxing is acceptable, you can box the returned future:
fn generate_func(val: u32) -> impl Fn(&u32) -> Pin<Box<dyn Future<Output = u32> + '_ + Send>> {
move |foo: &u32| Box::pin(async move { val + foo })
}
let func = generate_func(val);
The extra function is to force the types on the compiler, because, again, closure inference is quirky.