借用的值在线程使用时寿命不够长

Borrowed value does not live long enough when used by thread

所以我正在追求我的 Rust 冒险(喜欢它)并且我正在探索线程。像往常一样,我偶然发现了一个我不明白的错误。

这是一个最小的例子:

use std::thread;

pub fn compute_something(input: &Vec<&usize>) -> usize {
    input.iter().map(|v| *v).sum()
}

pub fn main() {
    let items = vec![0, 1, 2, 3, 4, 5];
    
    let mut slice: Vec<&usize> = Vec::new();

    slice.push(&items[1]); // borrowed value does not live long enough
    // argument requires that `items` is borrowed for `'static`
    slice.push(&items[2]); // borrowed value does not live long enough
    // argument requires that `items` is borrowed for `'static`

    assert_eq!(3, compute_something(&slice));
    
    let h = thread::spawn(move || compute_something(&slice));
    
    match h.join() {
        Ok(result) => println!("Result: {:?}", result),
        Err(e) => println!("Nope: {:?}", e)
    }
} // `items` dropped here while still borrowed

我当然做了一个playground来说明。

如果我删除线程部分(assert_eq! 行之后的所有内容)并调用 compute_something(&slice) 它编译正常。

这里主要有三点我不明白:

您将 slice 移动到传递给 thread::spawn() 的闭包中。由于传递给 thread::spawn() 的闭包必须是 'static,这意味着被移动到闭包中的向量不能借用任何不是 'static 的东西。因此,编译器将 slice 的类型推断为 Vec<&'static usize>.

但它确实借用了一些不是 'static 的东西——您试图推入其中的值是从 main() 的局部变量借用的,所以编译器会抱怨这个。

解决这种情况的最简单方法是让 slice 成为 Vec<usize> 而不是从 items 借用。

另一种选择是使用 scoped threads from the crossbeam crate,它知道如何通过强制所有线程在作用域结束之前连接来安全地从局部变量中借用。


直接回答您提出的问题:

Why is it a problem to drop items while borrowed at the end of the program, shouldn't the runtime clean-up the memory just fine?

main() 终止时,所有线程也将终止——但是,有一段短暂的 window 时间,在此期间 main() 的本地值已被销毁,但在 main() 之前线程被终止。 window 期间可能存在悬空引用,这违反了 Rust 的内存安全模型。这就是 thread::spawn() 需要 'static 闭包的原因。

即使您自己加入线程,借用检查器也不知道加入线程会结束借用。 (这是 crossbeam 的作用域线程解决的问题。)

What is still borrowing items at the end of the program?

移入闭包的向量仍在借用items

Why is calling compute_something from inside the thread's closure creating the issue and how do I solve it?

调用此函数不会产生问题。将 slice 移动到闭包中会产生问题。