您好,我在一个相当简单的测试用例中遇到了终身问题 我似乎无法理解编译器告诉我的内容。 下面的代码粘贴到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();
}
答案 0 :(得分:1)
编译器报告的每个note
都会将上下文添加到上一个错误或警告中。这不是一个单独的错误;修正错误会使笔记消失。
现在不幸的是,错误信息并非一目了然。问题是您尝试将e
Box<&'a Element>
移动到传递给thread::spawn
的闭包中,但thread::spawn
需要关闭有效期为'static
;即它不包含短于'static
的引用('static
是可用的最长生命周期:它对应于整个程序的持续时间)。 'a
不能保证等于'static
,因此错误。
您的代码无效,因为线程可能在引用的Element
被释放后继续运行。
一种可能的解决方案是不使用elements
向量中的引用:使用Vec<Box<Element>>
。但是,只有在您之后不需要重复使用元素时,这才有效。
另一种选择是使用线程安全的引用计数器(Arc
)而不是简单的引用。这消除了生命周期问题,但引入了一些运行时开销。
最后一个选项是使用crossbeam
到spawn scoped个主题。使用此选项,您可以继续使用引用。您可以做的一件事是在范围结束之前返回连接句柄。您仍然可以从Pipeline::run
返回连接句柄,但crossbeam
将在scope
返回之前加入所有线程。
历史记录:就在Rust达到1.0之前,标准库中曾经存在作用域线程的实现。但是不是为范围使用闭包,它只返回JoinHandle
,并且此实现的安全性依赖于调用JoinHandle
上的析构函数的程序。如果析构函数没有在适当的时间运行(例如,您调用mem::forget
),则线程将继续运行并可能引用释放的对象。 crossbeam
仍然依赖于被调用的析构函数,但它的编写使得库的用户永远无法获得Scope
的所有权,因此库可以保证析构函数将被运行。