Bruce
Bruce

Reputation: 623

Rust lifetime issue brought on by Mutex of data

I've narrowed this as much as I can. I have a vector of references to Senders and want to process them after receive() has completed running. However I'm running into a lifetime management issue. The inner struct is Mutex protected and contains the Senders that I'm trying to reference. I use the Mutex to gain mutability of the data, I'm open to alternatives. Any suggestions on how to work around this? I'd prefer not to change the signature of receive().

use std::sync::Mutex;
use std::sync::mpsc;

#[derive(Default)]
struct SaveForLater<'a> {
    queue: Vec<(&'a mpsc::Sender<usize>, usize)>,
}
impl<'a> SaveForLater<'a> {
    fn send(&mut self, channel: &'a mpsc::Sender<usize>, value: usize) {
        self.queue.push((channel, value));
    }
}

#[derive(Default)]
struct Forwarder {
    data: Mutex<ForwarderData>,
}
#[derive(Default)]
struct ForwarderData {
    senders: Vec<mpsc::Sender<usize>>,
}

impl Forwarder {
    fn with_capacity(capacity: usize) -> Self {
        let mut senders = Vec::new();
        for _ in 0..capacity {
            let (s,r) = mpsc::channel();
            senders.push(s);
        }
        let data = ForwarderData { senders };
        let data = Mutex::new(data);
        Self { data }
    }

    fn receive<'a>(&'a self, value: usize, sender: &mut SaveForLater<'a>) {
        match value {
            0 => { self.data.lock().unwrap().senders.drain(..); },
            _ => {
                let data = self.data.lock().unwrap();
                sender.send(&data.senders[0], value); },
/*
error[E0597]: `data` does not live long enough
  --> src/main.rs:40:30
   |
35 |     fn receive<'a>(&'a self, value: usize, sender: &mut SaveForLater<'a>) {
   |                -- lifetime `'a` defined here
...
40 |                 sender.send(&data.senders[0], value); },
   |                 -------------^^^^-------------------  - `data` dropped here while still borrowed
   |                 |            |
   |                 |            borrowed value does not live long enough
   |                 argument requires that `data` is borrowed for `'a`
*/
        }
    }
}

fn main() {
    let fwd = Forwarder::with_capacity(3);
    {
        let mut sender = SaveForLater::default();
        let value: usize = 42;
        fwd.receive(value, &mut sender);
        for (s,v) in sender.queue {
            s.send(v);
        }   
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_main() {
        main();
    }
}

see error in playground

Upvotes: 1

Views: 204

Answers (1)

trent
trent

Reputation: 27885

Mutex is a distraction here; the real problem is trying to store references in structs. Let's look at the documentation of mpsc::Sender to see how it's supposed to be used:

The sending-half of Rust's asynchronous channel type. This half can only be owned by one thread, but it can be cloned to send to other threads.

Sender does not even implement Sync, so even if you make the borrow checker happy you won't be able to share a single one between multiple threads. It's meant to be cloned instead:

struct SaveForLater {
    queue: Vec<(mpsc::Sender<usize>, usize)>,
}
    fn receive(&self, value: usize, sender: &mut SaveForLater) {
        // ...
                sender.send(data.senders[0].clone(), value);
                //                         ^^^^^^^^

Here's a full example. No lifetime parameters are necessary.

In general you should avoid putting references in structs because it can only be done safely if you are very diligent about proving the lifetime relationships at compile time. In most cases you should simply clone the data, or if that's the wrong semantic, use Arc or Rc. mpsc::Sender uses Arc internally to be cheap to clone.

Upvotes: 1

Related Questions