Miguel Guerrero
Miguel Guerrero

Reputation: 11

Error with Rayon, cannot borrow `*self` as mutable, as it is a captured variable in a `Fn` closure

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

Upvotes: 0

Views: 1269

Answers (1)

JMAA
JMAA

Reputation: 2049

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.

Upvotes: 1

Related Questions