Glenn Pierce
Glenn Pierce

Reputation: 950

Lifetime errors I don't know how to solve

Hi I am having issues with lifetimes in a fairly simple test case and I can't seem to understand what the compiler is telling me. The code below pastes into Rust playground.

The idea about the code is that Elements are strung together into a pipeline. Data eventually will be pass one one element to the next. Each stage has a (rx, tx) pair on which it receives input from previous stage and sends data to next stage. I can mark the Elements as Sync as only one Element will ever process one part of the data at one time.

The errors are:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> <anon>:56:18
   |
56 |         for e in self.elements {
   |                  ^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime 'a as defined on the body at 53:53...
  --> <anon>:53:54
   |
53 |     pub fn run(&self) -> Vec<thread::JoinHandle<()>> {
   |                                                      ^
note: ...so that expression is assignable (expected std::vec::Vec<Box<&Element>>, found std::vec::Vec<Box<&'a Element + 'a>>)
  --> <anon>:56:18
   |
56 |         for e in self.elements {
   |                  ^^^^^^^^^^^^^
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `[closure@<anon>:62:41: 64:15 e:Box<&Element>, sender:std::sync::Arc<std::sync::Mutex<std::sync::mpsc::SyncSender<(std::string::String, std::string::String)>>>, receiver:std::sync::Arc<std::sync::Mutex<std::sync::mpsc::Receiver<(std::string::String, std::string::String)>>>]` will meet its required lifetime bounds
  --> <anon>:62:27
   |
62 |              handles.push(thread::spawn(move || {
   |                           ^^^^^^^^^^^^^

The first error I am confused by as Elements are defined as &'a Element so should they not tell the compiler that they stick around for along as Pipeline?

The second error I guess is telling me Vec<thread::JoinHandle<()>> as to be told it depends on the lifetime 'a ? I am not sure how to convey that though.

I hope the third will make more sense after I correct the first two. At the moment I just don't know what its telling me.

use std::sync::{Arc, Mutex};
use std::thread;
use std::result::Result;
use std::sync::mpsc::{SyncSender, Receiver, sync_channel};

pub trait Element: Send + Sync {
    fn run(&self, output: Arc<Mutex<SyncSender<i32>>>,
                   input: Arc<Mutex<Receiver<i32>>>);
}

pub struct TestElement {}

impl TestElement {
    pub fn new() -> Self {
        TestElement {}
    }
}

impl Element for TestElement {
    fn run(&self, output: Arc<Mutex<SyncSender<i32>>>,
                   input: Arc<Mutex<Receiver<i32>>>) {
        println!("Hello");
    }
}

pub struct Pipeline<'a> {
    elements: Vec<Box<&'a Element>>,
}

impl<'a> Pipeline<'a> {
    pub fn new(name: String) -> Self {
        Pipeline {
            elements: Vec::new(),
        }
    }

    pub fn run(&self) -> Vec<thread::JoinHandle<()>> {
        let mut handles = Vec::with_capacity(self.elements.len());

        for e in self.elements {
             let channel = sync_channel::<i32>(1000);
             let sender = Arc::new(Mutex::new(channel.0)).clone();
             let receiver = Arc::new(Mutex::new(channel.1)).clone();

             handles.push(thread::spawn(move || {
                 e.run(sender, receiver);
             }));
        }

        handles
    }
}

fn main() {
    let mut test_element = TestElement::new();
    let mut pipeline = Pipeline::new("example pipeline".to_string());

    let handles = pipeline.run();
}

Upvotes: 0

Views: 544

Answers (1)

Francis Gagn&#233;
Francis Gagn&#233;

Reputation: 65937

Each note reported by the compiler adds context to the preceding error or warning. It's not a separate error; fixing the error will make the notes go away.

Now unfortunately, the error message isn't all that clear. The problem is that you're trying to move e, which is of type Box<&'a Element>, into the closure passed to thread::spawn, but thread::spawn requires a closure that is valid for the 'static lifetime; i.e. it doesn't contain references that are shorter than 'static (and 'static is the longest lifetime available: it corresponds to the duration of the whole program). 'a is not guaranteed to be equal to 'static, hence the error.

Your code is invalid because the thread may continue running after the Element the reference is referring to has been freed.

One possible solution is to simply not use references inside the elements vector: use Vec<Box<Element>>. That only works if you don't need to reuse the elements afterwards, though.

Another option is to use thread-safe reference counters (Arc) instead of simple references. This eliminates lifetime concerns, but introduces a bit of runtime overhead.

One last option is to use crossbeam to spawn scoped threads. With this option, you can continue using references. One thing you can't do with this is return the join handles before the scope has ended. You could still return the join handles from Pipeline::run, but crossbeam will join all the threads before scope returns.

Historical note: Just before Rust reached 1.0, there used to be an implementation of scoped threads in the standard library. But instead of using a closure for the scope, it would simply return a JoinHandle, and the safety of this implementation relied on the program calling the destructor on the JoinHandle. If the destructor wasn't run at the appropriate time (e.g. you called mem::forget), then the thread would keep running and potentially reference deallocated objects. crossbeam still relies on the destructor being called, but it's written so that users of the library can never obtain ownership of the Scope, so the library can guarantee that the destructor will be run.

Upvotes: 1

Related Questions