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

14

我目前在 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的方法?

对于这些幼稚的问题,我感到抱歉。我已经花了很多时间搜索,但我无法理解。可能是因为我的动态语言背景有些影响 :)

顺便问一下:将String转换为&str&input[..]是否可行?这是我找到的唯一稳定的方法。

2个回答

17
除了泄漏内存,您无法将&'a T转换为&'static T。幸运的是,这根本不必要。没有理由将借用指针发送到线程并在主线程上保留这些行。您不需要在主线程上保留这些行。只需发送这些行本身,即发送String

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

发送非'static引用之间的线程不安全,因为您永远不知道另一个线程将使用它多长时间,因此您不知道借用何时过期以及该对象何时可以释放。请注意,作用域线程没有这个问题(虽然不在1.0中,但正在进行重新设计),但常规的spawn线程存在此问题。

'static不是您应该避免使用的东西,对于它所做的事情来说完全没问题:标识值在程序运行期间一直存在。但是,如果这不是您要传达的内容,当然它就是错误的工具。


哦,好的。这是一个简单的解决方案。在struct中将&str替换为String就可以了。谢谢,我真的应该先尝试一下。 - marekventur

2
以这种方式考虑:一个线程没有语法生命周期,也就是说,线程不会在创建它的代码块结束时被放弃。无论您向线程发送什么数据,都必须确保它与线程一样长寿,这意味着永远存在。这意味着使用'static
在您的情况下,可能出现问题的是,如果主循环发送对线程的引用并在字符串被线程处理之前销毁了该字符串,则线程将访问无效内存。
一个选择是将您的行放入某个静态分配的容器中,但这意味着您永远不能销毁这些字符串。 一般来说,这是个坏主意。 另一个选择是思考:一旦读取了一行,主线程实际上是否需要该行? 如果主线程将责任转移到处理线程,则怎么样?
struct Message {
    text: String,
}

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

现在,您正在将所有权(移动)从主线程转移到处理程序线程。由于您移动了值,因此不涉及任何引用,并且不再适用于生命周期检查。"最初的回答"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接