Home > other >  How can I hold a boxed async function that captures variables?
How can I hold a boxed async function that captures variables?

Time:01-05

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

  • Related