我目前正在努力解决Rust(1.0)的生命周期问题,特别是在通过通道传递结构体时。
我如何得到这个简单的例子编译:
use std::sync::mpsc::{Receiver, Sender};
use std::sync::mpsc;
use std::thread::spawn;
use std::io;
use std::io::prelude::*;
struct Message<'a> {
text: &'a str,
}
fn main() {
let (tx, rx): (Sender<Message>, Receiver<Message>) = mpsc::channel();
let _handle_receive = spawn(move || {
for message in rx.iter() {
println!("{}", message.text);
}
});
let stdin = io::stdin();
for line in stdin.lock().lines() {
let message = Message {
text: &line.unwrap()[..],
};
tx.send(message).unwrap();
}
}
我:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:23:20
|
23 | text: &line.unwrap()[..],
| ^^^^^^^^^^^^^ does not live long enough
...
26 | }
| - temporary value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
我可以看到为什么会这样(line
只适用于for
的一次迭代),但我不知道这样做的正确方法是什么。
- 我应该,作为编译器提示,试图将
&str
转换成&'static str
? - 如果每一行都有
'static
生命周期,我是否会泄漏内存? - 我什么时候应该使用
'static
呢?这是我应该尽量避免的还是完全可以的? - 是否有更好的方法通过通道在结构体中传递
String
s ?
作为题外话:&input[..]
是否用于将String
转换为&str
?这是我能找到的唯一稳定的方法。
您不能将&'a T
转换为&'static T
,除非通过泄漏内存。幸运的是,这根本没有必要。没有理由将借来的指针发送给线程并将这些行保留在主线程中。你不需要主线程中的代码行。只发送行本身,即发送String
。
如果需要从多个线程访问(并且您不想克隆),请使用Arc<String>
(将来,Arc<str>
也可以)。这样,字符串在线程之间共享,正确地共享,因此当没有线程不再使用它时,它将被释放。
在线程之间发送非'static
引用是不安全的,因为你永远不知道其他线程将继续使用它多长时间,所以你不知道什么时候借用到期,对象可以被释放。请注意,作用域的线程不会有这个问题(1.0中没有,但我们正在重新设计),但常规的spawn
线程会。
'static
不是你应该避免的东西,它的作用是完美的:表示一个值在程序运行的整个过程中都存在。但如果这不是你想要传达的,那当然是错误的工具。
这样想:线程没有语法生命周期,即线程不会在创建它的代码块结束时被丢弃。无论向线程发送什么数据,都必须确保它和线程一样长,也就是永远。这意味着'static
。
在你的例子中,如果主循环向线程发送引用并在线程处理字符串之前销毁该字符串,则可能出现问题。处理字符串时,线程将访问无效内存。
一种选择是将行放入静态分配的容器中,但这意味着您永远无法销毁这些字符串。一般来说,这是个坏主意。另一种选择是考虑:主线程在读取该行之后是否真的需要该行?如果主线程将line的责任转移到处理线程会怎样?
struct Message {
text: String,
}
for line in stdin.lock().lines() {
let message = Message {
text: line.unwrap(),
};
tx.send(message).unwrap();
}
现在你正在将所有权(移动)从主线程转移到处理线程。因为您移动了值,所以不涉及引用,也不再应用生命周期检查。