用 par_iter() 替换 iter():不能在 Fn 闭包中捕获的外部变量中可变地借用数据 [英] Replace iter() with par_iter(): cannot borrow data mutably in a captured outer variable in an `Fn` closure

查看:62
本文介绍了用 par_iter() 替换 iter():不能在 Fn 闭包中捕获的外部变量中可变地借用数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望将 iter() 替换为 Rayon's par_iter() 在这样一个相当简单的情况下,但我没有这样做.

之前的代码:

indexes_to_increment.iter().for_each(|x| self.some_data[*x as usize] += 1);`

这是人造丝修改后的代码:

extern crate rayon;使用人造丝::前奏::*;fn 主(){让 mut a = SomeStruct::new(vec![1, 0, 0, 1]);a.add_factor_indexes(&vec![1, 2]);println!("{:?}", a);//吐出SomeStruct { some_data: [1, 1, 1, 1] }"}#[派生(调试)]struct SomeStruct {some_data: Vec<u8>,}impl SomeStruct {fn new(some_data: Vec) ->一些结构{SomeStruct { some_data }}fn add_factor_indexes(&mut self, index_to_increment: &[u8]) {//indexes_to_increment.iter().for_each(|x| self.some_data[*x as usize] += 1);索引到增量.par_iter().for_each(|x| self.some_data[*x as usize] += 1);}}

(游乐场)

虽然我知道以下错误消息告诉我该怎么做,但此时我无法这样做.

错误[E0387]:不能在`Fn`闭包中捕获的外部变量中可变地借用数据-->src/main.rs:23:27|23 |.for_each(|x| self.some_data[*x as usize] += 1);|^^^^^^^^^^^^^^^|help: 考虑改变这个闭包以通过可变引用获取 self-->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,index_to_increment: &[u8]) {self.some_data.par_iter_mut().枚举().filter(|&(i, _)| index_to_increment.contains(&(i as u8))).map(|(_, v)| v).for_each(|x| *x += 1);}

<块引用>

并且可以用一组替换

由于重复查找,如果数据量较大,我会推荐它.

I was hoping to replace an iter() with Rayon's par_iter() in a rather simple case like this, but I am failing to do so.

The previous code:

indexes_to_increment
    .iter()
    .for_each(|x| self.some_data[*x as usize] += 1);`

Here's the Rayon-modified code:

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);
    }
}

(playground)

While I know that the following error message is telling me what to do, at this point I'm unable to do so.

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);
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If I knew that the indexes_to_increment vector in add_factor_indexes only contains unique u8s and could be replaced with a set, would that change anything?

解决方案

This error message is an example of exactly the type of error prevention that Rust is designed to provide to you. Said another way, the compiler is preventing you from mutably accessing the same piece of memory concurrently.

Conceptually, the code you are trying to run should be safe because you are always accessing a completely disjoint piece of the vector, there won't be any overlapping mutable borrows of the same index, but the compiler cannot tell that. All it sees is that self.some_data is borrowed mutably multiple times; it doesn't know what the implementation of Index does or what the body of the closure does.

You could find all the matching slots in the vector and then iterate over all of the results:

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

I'd recommend it due to the repeated lookups, if it's a larger amount of data.

这篇关于用 par_iter() 替换 iter():不能在 Fn 闭包中捕获的外部变量中可变地借用数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆