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.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.