How to handle lifetime on async functions?

I'm trying to spawn an async function inside async function. I want to share the state between two function.

It'll porbably looks like this.

   async fn send_ping(&self, node: &Node) {
        let (sender, reciver) = oneshot::channel();
        runtime::task::spawn(async {
            match reciver.await {
                Ok(res) => {
                    match res {
                        AckRes::Ack(seq_no) => {
                            // stop the indirect timer
                        },
                        AckRes::Timeout => {
                            let indirect_ping = self.no_of_indirect_ping.load(atomic::Ordering::Relaxed);
                        },
                    }
                },
                Err(_) => {
                    panic!("we're not cancelling anywhere");
                },
            }
        });  
    }

But, I'm getting a lifetime issue.

error: cannot infer an appropriate lifetime
   --> src/main.rs:143:24
    |
143 |     async fn send_ping(&self, node: &Node) {
    |                        ^^^^^ ...but this borrow...
...
173 |         runtime::task::spawn(async {
    |         -------------------- this return type evaluates to the `'static` lifetime...
    |
note: ...can't outlive the lifetime '_ as defined on the method body at 143:24
   --> src/main.rs:143:24
    |
143 |     async fn send_ping(&self, node: &Node) {
    |                        ^
help: you can add a constraint to the return type to make it last less than `'static` and match the lifetime '_ as defined on the method body at 143:24
    |
173 |         runtime::task::spawn + '_(async {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^

Could anyone help me in this?

runtime::task::spawn requires an impl Future + 'static, basically a future that is disconnected from the current stack frame, so that it can continue running even after the current function has ended. async {} (like a closure) can borrow from the current stack frame, useful when you want to have concurrent sub-futures running as part of a future. To force the block to be disconnected you must use async move {} and ensure you don't move any references into the block.

Unfortunately in this case because you use a reference to self in the block I don't see any trivial way to satisfy this. You would have to either wrap self or no_of_indirect_ping into an Arc so that even once this function returns it's still valid to use.

1 Like

Depending on what you mean by this you might not need to spawn an independent future, you could instead have multiple sub-futures of this function running concurrently, e.g. using futures::join:

use futures::join;

async fn send_ping(&self, node: &Node) {
    let (sender, reciver) = oneshot::channel();

    let first = async {
        match reciver.await.expect("we're not cancelling anywhere") {
            AckRes::Ack(seq_no) => {
            },
            AckRes::Timeout => {
                let indirect_ping = self.no_of_indirect_ping.load(atomic::Ordering::Relaxed);
            },
        }
    };

    let second = async {
        sender.send(AckRes::Ack(5)).await;
    };

    join!(first, second);
}
1 Like

I want it as independent future.
self will contain

ack_handler: lock::Mutex<HashMap<u32,AckHandler>>,

The logic will go like this
If I get any udp response, I just sent ack to the oneshot channel.
Also, I spawn a timeout future. It'll check whether we go ack or not. If not then timeoiut future will send timeout signal.

I guess, I can use join!()

Thanks for the help

Join will always wait until both futures are finished. You could use select from the futures library. It will return whichever future resolves first, sounds ideal for a timeout to me. There is a futures-timer crate that you can probably use to implement the timeout.