将 iter() 替换为 par_iter():不能在“Fn”闭包中捕获的外部变量中可变地借用数据
Replace iter() with par_iter(): cannot borrow data mutably in a captured outer variable in an `Fn` closure
我希望在像这样一个相当简单的情况下用 Rayon's par_iter()
替换 iter()
,但我没有这样做。
上一个代码:
indexes_to_increment
.iter()
.for_each(|x| self.some_data[*x as usize] += 1);`
这是 Rayon 修改后的代码:
extern crate rayon;
use rayon::prelude::*;
fn main() {
let mut a = SomeStruct::new(vec![1, 0, 0, 1]);
a.add_factor_indexes(&vec![1, 2]);
println!("{:?}", a); // spits out "SomeStruct { some_data: [1, 1, 1, 1] }"
}
#[derive(Debug)]
struct SomeStruct {
some_data: Vec<u8>,
}
impl SomeStruct {
fn new(some_data: Vec<u8>) -> SomeStruct {
SomeStruct { some_data }
}
fn add_factor_indexes(&mut self, indexes_to_increment: &[u8]) {
//indexes_to_increment.iter().for_each(|x| self.some_data[*x as usize] += 1);
indexes_to_increment
.par_iter()
.for_each(|x| self.some_data[*x as usize] += 1);
}
}
虽然我知道以下错误消息告诉我该怎么做,但目前我无法这样做。
error[E0387]: cannot borrow data mutably in a captured outer variable in an `Fn` closure
--> src/main.rs:23:27
|
23 | .for_each(|x| self.some_data[*x as usize] += 1);
| ^^^^^^^^^^^^^^
|
help: consider changing this closure to take self by mutable reference
--> src/main.rs:23:23
|
23 | .for_each(|x| self.some_data[*x as usize] += 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
如果我知道 add_factor_indexes
中的 indexes_to_increment
向量只包含唯一的 u8
并且可以用集合替换,那会改变什么吗?
此错误消息是 确切 Rust 旨在为您提供的错误预防类型的示例。换句话说,编译器阻止您同时访问同一块内存。
从概念上讲,您尝试 运行 的代码应该是安全的,因为您总是访问向量的一个完全不相交的片段,不会有任何重叠的可变借用同一索引,但是编译器无法判断。它所看到的只是 self.some_data
被多次可变借用;它不知道 Index
的实现或闭包主体的作用。
您可以找到向量中所有匹配的位置,然后遍历所有结果:
fn add_factor_indexes(&mut self, indexes_to_increment: &[u8]) {
self.some_data
.par_iter_mut()
.enumerate()
.filter(|&(i, _)| indexes_to_increment.contains(&(i as u8)))
.map(|(_, v)| v)
.for_each(|x| *x += 1);
}
and could be replaced with a set
如果数据量较大,我会推荐它,因为反复查找。
Rayon 在线程不共享非常量数据时效果最佳。例如,如果每个传递给 par_iter
的闭包只对它自己的数据进行操作,并且在最后一步将它们重新组装在一起,Rayon 就不会抱怨。 (Google MapReduce 是此策略的一个流行示例,可以很好地扩展到云系统。)
除了 Shepmaster 提供的解决方案之外,修复代码的一种直接方法是从 Vec<u8>
切换到 Vec<AtomicUsize>
,并使用 fetch_add
方法增加索引.由于 fetch_add
接受了一个共享引用,Rayon 将接受它并且它会做你想做的事。
extern crate rayon;
use rayon::prelude::*;
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
let mut a = SomeStruct::new([1, 0, 0, 1].iter()
.map(|n| AtomicUsize::new(*n as usize)).collect());
a.add_factor_indexes(&vec![1, 2]);
println!("{:?}", a);
}
#[derive(Debug)]
struct SomeStruct {
some_data: Vec<AtomicUsize>,
}
impl SomeStruct {
fn new(some_data: Vec<AtomicUsize>) -> SomeStruct {
SomeStruct { some_data }
}
fn add_factor_indexes(&mut self, indexes_to_increment: &[u8]) {
indexes_to_increment
.par_iter()
.for_each(|x| {
self.some_data[*x as usize].fetch_add(1, Ordering::SeqCst);
});
}
}
我希望在像这样一个相当简单的情况下用 Rayon's par_iter()
替换 iter()
,但我没有这样做。
上一个代码:
indexes_to_increment
.iter()
.for_each(|x| self.some_data[*x as usize] += 1);`
这是 Rayon 修改后的代码:
extern crate rayon;
use rayon::prelude::*;
fn main() {
let mut a = SomeStruct::new(vec![1, 0, 0, 1]);
a.add_factor_indexes(&vec![1, 2]);
println!("{:?}", a); // spits out "SomeStruct { some_data: [1, 1, 1, 1] }"
}
#[derive(Debug)]
struct SomeStruct {
some_data: Vec<u8>,
}
impl SomeStruct {
fn new(some_data: Vec<u8>) -> SomeStruct {
SomeStruct { some_data }
}
fn add_factor_indexes(&mut self, indexes_to_increment: &[u8]) {
//indexes_to_increment.iter().for_each(|x| self.some_data[*x as usize] += 1);
indexes_to_increment
.par_iter()
.for_each(|x| self.some_data[*x as usize] += 1);
}
}
虽然我知道以下错误消息告诉我该怎么做,但目前我无法这样做。
error[E0387]: cannot borrow data mutably in a captured outer variable in an `Fn` closure
--> src/main.rs:23:27
|
23 | .for_each(|x| self.some_data[*x as usize] += 1);
| ^^^^^^^^^^^^^^
|
help: consider changing this closure to take self by mutable reference
--> src/main.rs:23:23
|
23 | .for_each(|x| self.some_data[*x as usize] += 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
如果我知道 add_factor_indexes
中的 indexes_to_increment
向量只包含唯一的 u8
并且可以用集合替换,那会改变什么吗?
此错误消息是 确切 Rust 旨在为您提供的错误预防类型的示例。换句话说,编译器阻止您同时访问同一块内存。
从概念上讲,您尝试 运行 的代码应该是安全的,因为您总是访问向量的一个完全不相交的片段,不会有任何重叠的可变借用同一索引,但是编译器无法判断。它所看到的只是 self.some_data
被多次可变借用;它不知道 Index
的实现或闭包主体的作用。
您可以找到向量中所有匹配的位置,然后遍历所有结果:
fn add_factor_indexes(&mut self, indexes_to_increment: &[u8]) {
self.some_data
.par_iter_mut()
.enumerate()
.filter(|&(i, _)| indexes_to_increment.contains(&(i as u8)))
.map(|(_, v)| v)
.for_each(|x| *x += 1);
}
and could be replaced with a set
如果数据量较大,我会推荐它,因为反复查找。
Rayon 在线程不共享非常量数据时效果最佳。例如,如果每个传递给 par_iter
的闭包只对它自己的数据进行操作,并且在最后一步将它们重新组装在一起,Rayon 就不会抱怨。 (Google MapReduce 是此策略的一个流行示例,可以很好地扩展到云系统。)
除了 Shepmaster 提供的解决方案之外,修复代码的一种直接方法是从 Vec<u8>
切换到 Vec<AtomicUsize>
,并使用 fetch_add
方法增加索引.由于 fetch_add
接受了一个共享引用,Rayon 将接受它并且它会做你想做的事。
extern crate rayon;
use rayon::prelude::*;
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
let mut a = SomeStruct::new([1, 0, 0, 1].iter()
.map(|n| AtomicUsize::new(*n as usize)).collect());
a.add_factor_indexes(&vec![1, 2]);
println!("{:?}", a);
}
#[derive(Debug)]
struct SomeStruct {
some_data: Vec<AtomicUsize>,
}
impl SomeStruct {
fn new(some_data: Vec<AtomicUsize>) -> SomeStruct {
SomeStruct { some_data }
}
fn add_factor_indexes(&mut self, indexes_to_increment: &[u8]) {
indexes_to_increment
.par_iter()
.for_each(|x| {
self.some_data[*x as usize].fetch_add(1, Ordering::SeqCst);
});
}
}