Tokio actor with generic message, channel message type needs to be 'static?

Hello,

I am trying to wire up a simple actor that is generic over the message type that it receives. I am getting the following error:

error[E0310]: the parameter type `T` may not live long enough
   --> src/ib_data.rs:47:20
    |
43  | impl<T: std::fmt::Debug + Send> ListenerHandle<T> {
    |      -- help: consider adding an explicit lifetime bound...: `T: 'static +`
...
47  |         let join = tokio::spawn(listen(listener));
    |                    ^^^^^^^^^^^^ ...so that the type `impl Future<Output = ()>` will meet its required lifetime bounds...

I am thinking that I must be missing something in my basic implementation if I am seeing this error. I was under the impression that 'static means the type must live for the duration of the runtime but I don't want to have to keep messages around for any longer than they are needed.

I've seen similar errors involving ownership semantics when a closure sent to a thread, since that closure may live longer than the thread which launched it. This error seems quite similar, but since its the message type of a channel that is generic I am having a hard time reasoning through this one. Here is the code:

struct Listener<T> {
    receiver: Receiver<T>,
}

impl<T: std::fmt::Debug + Send> Listener<T> {
    fn new(receiver: Receiver<T>) -> Self {
        Self { receiver }
    }
}

async fn listen<T: std::fmt::Debug + Send>(mut listener: Listener<T>) {
    while let Some(msg) = listener.receiver.recv().await {
        println!("{msg:?}")
    }
}

pub struct ListenerHandle<T> {
    sender: Sender<T>,
    join: JoinHandle<()>,
}

impl<T: std::fmt::Debug + Send> ListenerHandle<T> {
    pub fn new() -> Self {
        let (sender, receiver) = mpsc::channel(12);
        let listener = Listener { receiver: receiver };
        let join = tokio::spawn(listen(listener));
        Self { sender, join }
    }
}

Any pointers here would be greatly appreciated!

Dave

I think you're confusing lifetime parameters with lifetime bounds here. MyType<'static> means that MyType refers to something that lives as long as the program. But MyType: 'static means that a MyType is able to be stored as long as the program, since it owns all of its data. So for instance, we have i32: 'static and String: 'static, but we don't have &'a str: 'static or slice::Iter<'a>: 'static (unless 'a is 'static). In practice, T: 'static mostly means that T cannot be a reference, and it cannot contain any non-'static references in its fields.

2 Likes

Generally, T: 'a means that the type T may not be annotated with lifetimes shorter than 'a. So for example, a &'long u32 is 'short, but a &'short u32 is not 'long. Types like Vec<u8> that are not annotated with any lifetime satisfy : 'a for any lifetime 'a including 'static.

Saying that T: 'static does not impose a restriction that the value must live forever.

A common link for similar cases: rust-blog/common-rust-lifetime-misconceptions.md at master · pretzelhammer/rust-blog · GitHub

3 Likes

Great, thank you all. This makes great sense! Cheers.

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.