我目前正在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
结构?我为那些天真的问题道歉。我已经花了很长时间搜索,但我不能完全围绕它。可能是我的动态语言背景妨碍了:)
顺便说一句:将&input[..]
转换为String
的{{1}}是否合适?这是我能找到的唯一稳定的方法。
答案 0 :(得分:12)
除了泄漏内存外,您无法将&'a T
转换为&'static T
。幸运的是,这根本不是必需的。没有理由将借用的指针发送到线程并将线保留在主线程上。你不需要主线程上的线条。只需发送行自己,即发送String
。
如果的访问权限 是必要的(并且您不想克隆),请使用Arc<String>
(以后,Arc<str>
也可以使用) 。这样,字符串在线程之间共享,正确共享,因此当没有线程再次使用它时,它将被完全释放。
在线程之间发送非'static
引用是不安全的,因为您永远不知道其他线程将继续使用多长时间,因此您不知道借用何时到期并且可以释放该对象。请注意,作用域的线程没有这个问题(它们不在1.0中,但在我们说话的时候正在重新设计)确实允许这样做,但是常规spawn
个线程做。
'static
不是你应该避免的东西,它完全可以做它的作用:表示一个值在程序运行的整个持续时间内存在。但如果这不是你想要传达的东西,当然这是错误的工具。
答案 1 :(得分:0)
这样考虑:一个线程没有句法生存期,即该线程不会在创建它的代码块末尾被丢弃。无论将什么数据发送到线程,都必须确保它与线程一样长寿,这意味着永远。即'static
。
在您的情况下可能出现的问题是,如果主循环发送对线程的引用并在该线程处理该字符串之前销毁该字符串。处理该字符串时,线程将访问无效的内存。
一种选择是将您的行放入一个静态分配的容器中,但这意味着您永远无法销毁那些字符串。一般来说,这是个坏主意。另一种选择是思考:一旦读取主线程实际上是否需要该行?如果主线程将行的责任转移到处理线程上怎么办?
struct Message {
text: String,
}
for line in stdin.lock().lines() {
let message = Message {
text: line.unwrap(),
};
tx.send(message).unwrap();
}
现在,您正在将所有权(移动)从主线程转移到处理程序线程。因为您移动了价值,所以不涉及任何引用,也不再应用对生命周期的检查。