Reputation: 11
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
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