借用的值在线程使用时寿命不够长
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)
它编译正常。
这里主要有三点我不明白:
为什么在程序结束时借用时删除items
是个问题,运行时不应该清理记忆就好了?我无法访问 main
之外的 slice
。
节目最后还在借items
什么? slice
?如果是这样,为什么通过删除 assert_eq!
行之后的所有内容来编译同一个程序?我看不出它是如何改变借用模式的。
为什么从线程闭包内部调用 compute_something
会产生问题,我该如何解决?
您将 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
移动到闭包中会产生问题。
所以我正在追求我的 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)
它编译正常。
这里主要有三点我不明白:
为什么在程序结束时借用时删除
items
是个问题,运行时不应该清理记忆就好了?我无法访问main
之外的slice
。节目最后还在借
items
什么?slice
?如果是这样,为什么通过删除assert_eq!
行之后的所有内容来编译同一个程序?我看不出它是如何改变借用模式的。为什么从线程闭包内部调用
compute_something
会产生问题,我该如何解决?
您将 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
移动到闭包中会产生问题。