借用的值在测试方法中存在的时间不够长



我对Rust还很陌生(试图离开果朗(。所以我在Go中写了一段代码,并决定尝试在Rust中写。

实际代码如下:

use rand::Rng;
use std::vec::Vec;
use std::thread::{self, JoinHandle};
pub struct WorkManager {
pub threshold: i32,
}
impl WorkManager {
pub fn new(trs: i32) -> Self {
Self { threshold: trs }
}
pub fn run_job<T: Send + Sync>(&self, input: &'static Vec<T>, f: fn(&T) -> T) -> Vec<T> {
if input.len() > self.threshold as usize {
let mut guards: Vec<JoinHandle<Vec<T>>> = vec!();
for chunk in input.chunks(self.threshold as usize) {
let chunk = chunk.to_owned();
let g = thread::spawn(move || chunk.iter().map(|x| f(x)).collect());
guards.push(g);
};
let mut result: Vec<T> = Vec::with_capacity(input.len());
for g in guards {
result.extend(g.join().unwrap().into_iter());
}
result
} else {
return input.iter().map(|x| f(x)).collect::<Vec<T>>();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const TRS_TEST: i32 = 10;
fn testing_function(a: &u32) -> u32 {
return 3 * a;
}
#[test]
fn test_single() {
let wm: WorkManager = WorkManager::new(TRS_TEST);
let values: Vec<u32> = rand::thread_rng()
.sample_iter(&Uniform::from(0..20))
.take(TRS_TEST as usize - 1)
.collect();
assert_eq!(
wm.run_job(&values, testing_function),
values
.iter()
.map(|x| testing_function(x))
.collect::<Vec<u32>>()
);
}
}

所以问题是当我运行货物测试时:

error[E0597]: `values` does not live long enough
--> src/lib.rs:50:24
|
50 |             wm.run_job(&values, testing_function),
|             -----------^^^^^^^-------------------
|             |          |
|             |          borrowed value does not live long enough
|             argument requires that `values` is borrowed for `'static`
...
56 |     }
|     - `values` dropped here while still borrowed

首先,我在函数声明中没有使用"static",但我导致了我读了很多关于生命周期的书,但我似乎错过了一些东西,有什么想法吗?

看起来您想要以下行:

let chunk = chunk.to_owned();

chunk&[T]类型创建Vec<T>。但这并没有奏效。这里发生的事情很奇怪。

你想点击以下impl:

impl<T: Clone> ToOwned for [T]

让我们假设我们是编译器。我们为类型&[T]寻找一个名为to_owned()的方法。这个过程是如何运作的?你可以在What are Rust';精确的自动取消引用规则?,但在这种情况下会发生以下情况:

  1. 由于我们已经通过了&[T],我们首先对采用&[T]的方法进行循环。有这样一个——<[T] as ToOwned>::to_owned()&[T],因为ToOwned::to_owned()&self,而self[T]。这个方法实施了吗?嗯,是的,如果T: Clone。这样行吗?不,我们没有这个界限。继续前进
  2. 寻找一种采用&&[T]的方法。它存在吗?是的,<&[T] as ToOwned>::to_owned()。是否已实施?有一个覆盖impl<T: Clone> ToOwned for T,所以如果&[T]: Clone成立,它就会被实现。是吗?是的:每个共享引用都是Copy,每个Copy也是Clone(CopyClone作为超性状(

所以我们找到了<&&[T] as ToOwned>::to_owned()。这个方法返回什么?它返回&[T](因为<T: Clone as ToOwned>::Owned == T,在我们的例子中,T&[T](。总之,我们最终没有创建Vec<T>,而是创建了&[T]——或者只是复制了我们的参考文献,根本没有解决问题!

如果我们使用更明确的to_vec(),编译器的错误也会更有帮助:

error[E0277]: the trait bound `T: Clone` is not satisfied
--> src/lib.rs:31:35
|
31  |                 let chunk = chunk.to_vec();
|                                   ^^^^^^ the trait `Clone` is not implemented for `T`
|
note: required by a bound in `slice::<impl [T]>::to_vec`
help: consider further restricting this bound
|
27  |     pub fn run_job<T: Send + Sync + std::clone::Clone>(&self, input: &Vec<T>, f: fn(&T) -> T) -> Vec<T> {
|                                   +++++++++++++++++++

应用这个建议并没有结束这个故事,尽管我们提出了:

error[E0310]: the parameter type `T` may not live long enough
--> src/lib.rs:32:25
|
27  |     pub fn run_job<T: Send + Sync + Clone>(&self, input: &Vec<T>, f: fn(&T) -> T) -> Vec<T> {
|                    -- help: consider adding an explicit lifetime bound...: `T: 'static +`
...
32  |                 let g = thread::spawn(move || chunk.iter().map(|x| f(x)).collect());
|                         ^^^^^^^^^^^^^ ...so that the type `[closure@src/lib.rs:32:39: 32:83]` will meet its required lifetime bounds...
|
note: ...that is required by this bound
error[E0310]: the parameter type `T` may not live long enough
--> src/lib.rs:32:25
|
27  |     pub fn run_job<T: Send + Sync + Clone>(&self, input: &Vec<T>, f: fn(&T) -> T) -> Vec<T> {
|                    -- help: consider adding an explicit lifetime bound...: `T: 'static +`
...
32  |                 let g = thread::spawn(move || chunk.iter().map(|x| f(x)).collect());
|                         ^^^^^^^^^^^^^ ...so that the type `Vec<T>` will meet its required lifetime bounds...
|
note: ...that is required by this bound

发生这种情况是因为T可以间接地包含一个生存期,例如它可以是&'something_non_static i32。添加绑定的T: 'static + ...确实修复了所有错误,现在它可以工作了!

这是我们能做的最好的事情吗?绝对没有。我们正在复制我们的数据,我们并不真的需要它——我们这样做只是为了满足借款检查器的要求!

由于我们并不真正需要我们的数据是'static,而且我们只是因为使用线程而被迫使用它,所以我们最好的选择是使用交叉梁的作用域线程(它们也将是std的一部分(:

pub fn run_job<T: Send + Sync>(&self, input: &[T], f: impl Fn(&T) -> T + Sync) -> Vec<T> {
if input.len() > self.threshold as usize {
crossbeam::scope(|scope| {
let mut guards = vec![];
for chunk in input.chunks(self.threshold as usize) {
let g = scope.spawn(|_| chunk.iter().map(|x| f(x)).collect::<Vec<_>>());
guards.push(g);
}
guards.into_iter().flat_map(|g| g.join().unwrap()).collect()
})
.unwrap()
} else {
input.iter().map(|x| f(x)).collect()
}
}

注意,我还修复了一些不规则的部分:例如,传递&Vec是一个反模式——而是传递一个切片。还有更少的类型注释,更多的迭代器,impl Fn而不是fn,等等

作用域线程允许我们共享非'static数据,代价是我们的线程必须在返回之前完成(在删除作用域对象之前,它将隐式地join()(。既然我们无论如何都这么做了,那也没关系。

但我们只是在重新发明轮子。有一个库可以自动将工作拆分为线程,并且使用高级的工作窃取技术(人造丝!使用它很简单:

pub fn run_job<T: Send + Sync>(&self, input: &[T], f: impl Fn(&T) -> T + Send + Sync) -> Vec<T> {
use rayon::prelude::*;
input.par_iter().map(f).collect()
}

是的,仅此而已(请注意,这并不能保证订单(!

因此,在您试图调用的函数中,指针/引用被标记为静态生存期。这意味着,对于整个程序,该指针必须是活动的。所以,我真的不知道如何在这里完全修复它,因为.clouds方法需要静态生存期:(对不起

最新更新