终身错误我不知道如何解决

时间:2017-03-05 09:04:43

标签: rust

您好,我在一个相当简单的测试用例中遇到了终身问题 我似乎无法理解编译器告诉我的内容。 下面的代码粘贴到Rust playground。

关于代码的想法是Element被串在一起成为一个管道。数据最终将通过一个元素传递给下一个元素。每个阶段都有一个(rx, tx)对,它接收来自前一阶段的输入并将数据发送到下一阶段。我可以将Element标记为Sync,因为只有一个Element会一次处理一部分数据。

错误是:

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 || {
   |                           ^^^^^^^^^^^^^

我感到困惑的第一个错误是Element s被定义为 &'a Element所以他们不应该告诉编译器他们会坚持下去 对于Pipeline

我猜第二个错误告诉我Vec<thread::JoinHandle<()>>被告知它取决于生命周期'a?我不知道如何表达这一点。

我希望在纠正前两个之后第三个会更有意义。目前我还不知道它告诉我什么。

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();
}

1 个答案:

答案 0 :(得分:1)

编译器报告的每个note都会将上下文添加到上一个错误或警告中。这不是一个单独的错误;修正错误会使笔记消失。

现在不幸的是,错误信息并非一目了然。问题是您尝试将e Box<&'a Element>移动到传递给thread::spawn的闭包中,但thread::spawn需要关闭有效期为'static;即它不包含短于'static的引用('static是可用的最长生命周期:它对应于整个程序的持续时间)。 'a不能保证等于'static,因此错误。

您的代码无效,因为线程可能在引用的Element被释放后继续运行。

一种可能的解决方案是不使用elements向量中的引用:使用Vec<Box<Element>>。但是,只有在您之后不需要重复使用元素时,这才有效。

另一种选择是使用线程安全的引用计数器(Arc)而不是简单的引用。这消除了生命周期问题,但引入了一些运行时开销。

最后一个选项是使用crossbeamspawn scoped个主题。使用此选项,您可以继续使用引用。您可以做的一件事是在范围结束之前返回连接句柄。您仍然可以从Pipeline::run返回连接句柄,但crossbeam将在scope返回之前加入所有线程。

历史记录:就在Rust达到1.0之前,标准库中曾经存在作用域线程的实现。但是不是为范围使用闭包,它只返回JoinHandle,并且此实现的安全性依赖于调用JoinHandle上的析构函数的程序。如果析构函数没有在适当的时间运行(例如,您调用mem::forget),则线程将继续运行并可能引用释放的对象。 crossbeam仍然依赖于被调用的析构函数,但它的编写使得库的用户永远无法获得Scope的所有权,因此库可以保证析构函数将被运行。