Rayon 错误,不能借用“*self”作为可变的,因为它是“Fn”闭包中捕获的变量

发布于 2025-01-16 05:40:55 字数 1453 浏览 2 评论 0原文

首先,我是 Rust 新手。对于我正在开发的游戏,我需要调用函数来更改矢量上的像素,然后将其转换为图像。为此,我有一个包含游戏所有上下文的结构,包括所述图像以及计算像素所需的所有内容。

这适用于单线程,但当我尝试将其与 Rayon 并行时则不行。

这是一个简化的代码作为问题的示例。它与 chunks_mut 一起使用。

use ::rayon::prelude::*;
struct Game {
    pub pixel: [u8; 4],
    pub pixel_vec: Vec<u8>,
}

impl Game {
    pub fn new(pixel: [u8; 4], length: usize) -> Self {
        let pixel_vec = vec![2; length];

        Self { pixel, pixel_vec }
    }
    pub fn draw(&mut self) {
        let mut pixel_vec = std::mem::take(&mut self.pixel_vec);

        pixel_vec
            .par_chunks_mut(4)
            .for_each(|slice| self.some_function(slice));
        self.pixel_vec = pixel_vec;
    }

    fn some_function(&mut self, slice: &mut [u8]) {
        for i in 0..slice.len() {
            slice[i] = self.pixel[i] * self.pixel[i];
        }
    }
}

fn main() {
    let mut game = Game::new([0, 3, 0, 0], 20);
    game.draw();
}

错误

Compiling playground v0.0.1 (/playground)
error[E0596]: cannot borrow `*self` as mutable, as it is a captured variable in a `Fn` closure
  --> src/main.rs:18:31
   |
18 |             .for_each(|slice| self.some_function(slice));
   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable

For more information about this error, try `rustc --explain E0596`.
error: could not compile `playground` due to previous error

First of all, I'm new toRust. For a game I'm working on I need to call function to change pixels on a vector that is then transformed into an iamge. For that, I have a struct that contains all the context of the game, include said image and everything I need to calcaulate the pixels.

This works with a single thread but not when I try to parallelize it with Rayon.

Here's a simplified code as an example of the problem. It works with chunks_mut.

use ::rayon::prelude::*;
struct Game {
    pub pixel: [u8; 4],
    pub pixel_vec: Vec<u8>,
}

impl Game {
    pub fn new(pixel: [u8; 4], length: usize) -> Self {
        let pixel_vec = vec![2; length];

        Self { pixel, pixel_vec }
    }
    pub fn draw(&mut self) {
        let mut pixel_vec = std::mem::take(&mut self.pixel_vec);

        pixel_vec
            .par_chunks_mut(4)
            .for_each(|slice| self.some_function(slice));
        self.pixel_vec = pixel_vec;
    }

    fn some_function(&mut self, slice: &mut [u8]) {
        for i in 0..slice.len() {
            slice[i] = self.pixel[i] * self.pixel[i];
        }
    }
}

fn main() {
    let mut game = Game::new([0, 3, 0, 0], 20);
    game.draw();
}

Error

Compiling playground v0.0.1 (/playground)
error[E0596]: cannot borrow `*self` as mutable, as it is a captured variable in a `Fn` closure
  --> src/main.rs:18:31
   |
18 |             .for_each(|slice| self.some_function(slice));
   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable

For more information about this error, try `rustc --explain E0596`.
error: could not compile `playground` due to previous error

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

一念一轮回 2025-01-23 05:40:55

您的 Game::some_function 函数使用 &mut self 接收器。这意味着它必须可变地借用self。也就是说,self.some_function(slice)Game::some_function(&mut self, slice) 相同。

因此,当您在闭包中调用 self.some_function(slice) 时,闭包必须将 self 捕获为可变的,因为它需要借用 self 作为调用该函数是可变的。但是,for_each 函数需要一个 Fn 闭包,而 Fn 闭包不会让您捕获为可变的(有充分的理由)。

您的情况的解决方案是将 some_function 中的接收器更改为 &self,因为按照编写,该函数不需要对 self 进行可变访问>:

fn some_function(&self, slice: &mut [u8]) {
    // ...
}

编辑:

要完成解释:人造丝 par_chunks_mut 方法返回一个实现 ParallelIterator,这是 for_each 方法的来源。这就是需要实现 Fn 的参数的地方。

现在考虑 Rust 中的可用的闭包特征,为什么是Fn?其他选项是 FnOnce 或 FnMut。 FnOnce 在调用一次时会消耗闭包,因此最多只能调用一次(因此得名),显然这不适用于 for_each 方法。 FnMut 较弱,它可以可变地捕获变量,那么为什么 rayon 不使用它呢?重点是在不同的线程中同时调用这个闭包,如果你改变它的状态,你就不能这样做,你必须同步可变访问,这将有效地序列化操作,使整个事情浪费时间。因此,Fn 是唯一的选项,并且不允许进行可变捕获。

Your Game::some_function function uses the &mut self receiver. This means that it must borrow self mutably. That is, self.some_function(slice) is the same as Game::some_function(&mut self, slice).

So when you call self.some_function(slice) in your closure, the closure must capture self as mutable, since it needs to borrow self as mutable to call the function. However, the for_each function requires a Fn closure and a Fn closure will not let you capture as mutable (for good reason).

The solution in your case is to change the receiver in some_function to &self, since as written that function doesn't need mutable access to self:

fn some_function(&self, slice: &mut [u8]) {
    // ...
}

Edit:

To complete the explanation: the rayon par_chunks_mut method returns a type that implements ParallelIterator, which is where the for_each method comes from. That's where the argument implementing Fn is required.

Now thinking about the available closure traits in Rust, why Fn? Well the other options are FnOnce or FnMut. FnOnce consumes the closure when called once and thus can only be called at most once (hence the name), clearly this isn't going to work for a for_each method. FnMut is weaker, it can capture variables mutably, so why does rayon not use that? The entire point is to call this closure concurrently in different threads, which you can't do if you're mutating its state, you'd have to synchronise the mutable access which would effectively serialise the operation, making the whole thing a waste of time. Thus, Fn is the only option and no mutable captures are allowed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文