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