Lifetime help needed for spawing a task

The idea of my code is running two concurrent tasks and reading messages from one of them. The other one listens for a stop signal which will make both the concurrent tasks halt. However when I am trying to spawn this task from my main thread, compiler says that the struct that does the tasks does not live long enough.

use std::time::Duration;

use tokio::sync::{
    mpsc::{unbounded_channel, UnboundedSender},
    oneshot::{channel, Receiver},
};

struct Actor {}

impl Actor {
    async fn act(&self, mut stop_listener: Receiver<()>, result_channel: UnboundedSender<u64>) {
        let part_1_jh = async {
            loop {
                if stop_listener.try_recv().is_ok() {
                    return;
                }
                println!("Hello from p1");
                tokio::time::sleep(Duration::from_millis(400)).await;
            }
        };

        let part_2_jh = async {
            loop {
                println!("Hello from p2");
                result_channel.send(43).unwrap();
                tokio::time::sleep(Duration::from_millis(700)).await;
            }
        };

        tokio::join!(part_1_jh, part_2_jh);
    }
}

#[tokio::main]
async fn main() {
    let (stop_tx, stop_rx) = channel::<()>();
    let (result_tx, mut result_rx) = unbounded_channel::<u64>();
    let actor = Actor {};

    tokio::task::spawn(actor.act(stop_rx, result_tx)); // problematic

    let mut total = 0;
    loop {
        if let Ok(n) = result_rx.try_recv() {
            if total < 100 {
                total += n;
            } else {
                stop_tx.send(()).unwrap();
                break;
            }
        }
    }

    println!("{total}");
}

The error message: Rust Playground

How can I circumvent this?

If you read the documentation of tokio::task::spawn(), you can see that it requires its future argument to be 'static, i.e., it should not contain any temporary references. Make the future own its data instead:

tokio::task::spawn(async move {
    actor.act(stop_rx, result_tx).await
});
2 Likes

As you already noticed the error stems from the &self argument in your act method. Since your Actor instance does not live for 'static, it can't be passed as a reference into a future spawned in a new tokio task. If you can't pass a reference that lives for 'static, you have to take ownership instead.

Alternatively to H2CO3's answer, you can change the argument in Actor::act from &self to self, which would also resolve your issue by moving ownership of the actor instance from your main function to the spawned task.