I want to have a boxed async function from a closure that can capture variables:
use std::future::Future;
use std::pin::Pin;
fn constrain<Fun>(fun: Fun) -> Fun
where
Fun: for<'arg> Fn(&'arg usize, &'arg usize) -> Pin<Box<dyn Future<Output = usize> 'arg>>,
{
fun
}
struct Test {}
impl Test {
pub async fn fun<'test, 'arg>(&'test self, val0: &'arg usize, val1: &'arg usize) -> usize
where
'arg: 'test,
{
*val0 *val1
}
}
pub async fn fun<'arg>(val0: &'arg usize, val1: &'arg usize) -> usize {
val0 val1
}
#[tokio::main(flavor = "current_thread")]
async fn main() {
let test = Test {};
let boxed = Box::new(constrain(move |v0, v1| {
let ret = Box::pin(async {
test.fun(v0, v1).await
//fun(v0, v1).await
});
ret
}));
let v0 = 10;
let v1 = 20;
let ret = boxed(&v0, &v1).await;
println!("{ret}");
}
When I call 'fun', it works, because it doesn't capture 'test'. When I call 'test.fun(...)', it fails:
error: lifetime may not live long enough
--> src/main.rs:46:9
|
38 | let boxed = Box::new(constrain(move |v0, v1|
| -------------
| | |
| | has type `&'2 usize`
| lifetime `'1` represents this closure's body
...
46 | ret
| ^^^ closure was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure
error: could not compile `playground` due to previous error
I don't know how to constrain the closure's lifetime to the arguments.
CodePudding user response:
Here's something that gets close:
use std::pin::Pin;
use std::future::Future;
#[macro_use]
extern crate derive_new;
#[derive(new)]
struct Test
{
}
impl Test
{
pub async fn fun<'test, 'arg>(&'test self, val0: &'arg usize, val1: &'arg usize) -> usize
where 'arg: 'test
{
*val0 *val1
}
}
trait ClosureFn
{
fn call<'myself, 'arg>(&'myself self, v0: &'arg usize, v1: &'arg usize) -> Pin<Box<dyn Future<Output = usize> 'myself>>
where 'arg: 'myself;
}
#[derive(new)]
struct Closure
{
test: Test
}
impl ClosureFn for Closure
{
fn call<'myself, 'arg>(&'myself self, v0: &'arg usize, v1: &'arg usize) -> Pin<Box<dyn Future<Output = usize> 'myself>>
where 'arg: 'myself
{
Box::pin(async
{
self.test.fun(v0, v1).await
})
}
}
#[tokio::main(flavor = "current_thread")]
async fn main()
{
let test = Test::new();
let boxed: Box<dyn ClosureFn> = Box::new(Closure::new(test));
let v0 = 10;
let v1 = 20;
let ret = boxed.call(&v0, &v1).await;
println!("{ret}");
}
Unfortunatelly, you need to create the 'Closure' structure, and call the function via 'call'.
CodePudding user response:
I was missing a move at the async function, I got confused by the compiler's error message:
use std::future::Future;
use std::pin::Pin;
fn constrain<Fun>(fun: Fun) -> Fun
where
Fun: for<'arg> Fn(&'arg usize, &'arg usize) -> Pin<Box<dyn Future<Output = usize> 'arg>>,
{
fun
}
#[derive(Clone)]
struct Test {}
impl Test
{
pub async fn fun<'test, 'arg>(&'test self, val0: &'arg usize, val1: &'arg usize) -> usize
where
'arg: 'test,
{
*val0 *val1
}
}
pub async fn fun<'arg>(val0: &'arg usize, val1: &'arg usize) -> usize {
val0 val1
}
#[tokio::main(flavor = "current_thread")]
async fn main()
{
let test = Test{};
let boxed = Box::new(constrain(move |v0, v1|
{
let test = test.clone();
let ret = Box::pin(async move
{
test.fun(v0, v1).await
//fun(v0, v1).await
});
ret
}));
let v0 = 10;
let v1 = 20;
let ret = boxed(&v0, &v1).await;
println!("{ret}");
}
I found the solution here, it happened to someone else too: https://users.rust-lang.org/t/lifetimes-issue-while-storing-rust-async-closure-functions-for-later-invocation/52232/2