如何对线程使用静态生命周期?

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 的一次迭代),但我不知道这样做的正确方法是什么。

对于那些幼稚的问题,我深表歉意。我已经花了很多时间搜索,但我无法完全理解它。这可能是我的动态语言背景妨碍了:)

顺便说一句:将 String 转换为 &str&input[..] 是否正常?这是我能找到的唯一稳定的方法。

除非泄漏内存,否则无法将 &'a T 转换为 &'static T。幸运的是,这根本没有必要。没有理由向线程发送借用的指针并保留主线程上的行。您不需要主线程上的行。只需自己发送行,即发送 String.

如果需要从多个线程访问(并且您不想克隆),请使用Arc<String>(将来,Arc<str>可能也工作)。这样字符串在线程之间共享,正确地共享,这样当没有线程再使用它时它就会被释放。

在线程之间发送非'static 引用是不安全的,因为您永远不知道另一个线程将继续使用它多长时间,所以您不知道借用何时到期以及对象何时可以被释放。请注意 scoped threads 没有这个问题(它不在 1.0 中,但正如我们所说的那样正在重新设计)确实允许这个,但是常规的 spawned 线程会.

'static 不是你应该避免的东西,它的作用非常好:表示一个值在程序 运行 的整个持续时间内都存在。但是,如果这不是您要传达的内容,那当然是错误的工具。

这样想:线程没有语法生命周期,即线程不会在创建它的代码块末尾被删除。无论您发送给线程的数据是什么,您都必须确保它会与线程一样存在,这意味着永远存在。这意味着 'static.

您的情况可能会出错,如果主循环发送对线程的引用并在线程处理字符串之前将其销毁。线程在处理字符串时会访问无效内存。

一种选择是将您的行放入某个静态分配的容器中,但这意味着您永远无法破坏这些字符串。一般来说是个坏主意。另一种选择是思考:主线程在读取后​​是否真的需要该行?如果主线程将行的责任转移给处理线程怎么办?

struct Message {
    text: String,
}
for line in stdin.lock().lines() {
    let message = Message {
        text: line.unwrap(),
    };
    tx.send(message).unwrap();
}

现在您正在将所有权从主线程转移(移动)到处理程序线程。因为您移动了您的价值,所以不涉及任何引用,也不再适用生命周期检查。