Using Rayon - Captured Variable in "Fn" enclosure

Hello!

So I am trying to learn how to use Rayon, and this is a simple MWE of something I would like to do:

extern crate rayon; // 1.3.0
use rayon::prelude::*;

fn main(){
    let mut v = vec![0,100];
    
    v
    .par_iter()
    .enumerate()
    .for_each
    (|(i,_)|
        {
            v[i] = i;
        }
    );
}

When I do this, I get the error:

   Compiling playground v0.0.1 (/playground)
error[E0596]: cannot borrow `v` as mutable, as it is a captured variable in a `Fn` closure
  --> src/main.rs:13:13
   |
13 |             v[i] = i;
   |             ^ cannot borrow as mutable

error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
  --> src/main.rs:11:6
   |
7  |     v
   |     - immutable borrow occurs here
...
10 |     .for_each
   |      -------- immutable borrow later used by call
11 |     (|(i,_)|
   |      ^^^^^^^ mutable borrow occurs here
12 |         {
13 |             v[i] = i;
   |             - second borrow occurs due to use of `v` in closure

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0502, E0596.
For more information about an error, try `rustc --explain E0502`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

And basically I can't figure out how to make this operation possible using Rayon. Hope someone can help out.

Kind regards

When inside rayon, you can only modify variables through the parameters in the closure. This is for thread-safety reasons.

fn main(){
    let mut v = vec![0; 100];
    
    v.par_iter_mut()
        .enumerate()
        .for_each(|(i, ptr)| {
            // This uses the closure parameter instead.
            *ptr = i;
        });
    
    println!("{:?}", v);
}

playground

Thanks! Exactly what I needed to move on. For my more advanced case, I would have two vectors, so for example something like this;

extern crate rayon; // 1.3.0
use rayon::prelude::*;

fn main(){
    let mut v = vec![0; 100];
    let mut u = vec![0; 100]
    
    v.par_iter_mut()
        .enumerate()
        .for_each(|(i, ptr)| {
            // This uses the closure parameter instead.
            *ptr = i;
            *u_ptr = 2*i;
        });
    
    println!("{:?}", v);
    println!("{:?}", u);
}

Written in NON-functional code to show my intend. How would I go about this, would combining them into a struct work, or is there a better way?

Kind regards

You would have to zip the iterators together like this. You can only use the vectors directly if the vector is not modified for the entire parallel operation.

fn main(){
    let mut v = vec![0; 100];
    let mut u = vec![0; 100];
    
    v.par_iter_mut()
        .zip(u.par_iter_mut())
        .enumerate()
        .for_each(|(i, (v_ptr, u_ptr))| {
            // This uses the closure parameter instead.
            *v_ptr = i;
            *u_ptr = 2*i;
        });
    
    println!("{:?}", v);
    println!("{:?}", u);
}

playground

1 Like

Tip: zip uses IntoParallelIterator, and the implementation for &mut T calls par_iter_mut(), so you can write that more concisely as zip(&mut u).

Thanks! Was exactly what I sought after, so cool to see how simple it is.

@cuviper I have used your suggestion to make it a bit neater to look at.

Kind regards

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.