Rayon par_iter().for_each trying to mutate a captured variable

With the below code I get the compiler error:
"cannot borrow sum1 as mutable, as it is a captured variable in a Fn closure"
How can I do what I want without the compiler complaining?

use ndarray::prelude::*;
use rayon::prelude::*;

fn func(x:f64, a1:f64, a2:f64, a3:f64) -> f64 {
    x+a1*a2+a3
}

fn sum_over() -> (Vec::<Array1::<f64>>,Vec::<Array1::<f64>>) {
    let ni: usize = 100;
    let a1 = Array1::<f64>::zeros(ni) + 2.0;
    let a2 = Array1::<f64>::zeros(ni) + 2.0;
    let a3 = Array1::<f64>::zeros(ni) + 2.0;
    let a4 = Array1::<f64>::zeros(ni) + 2.0;
    let a5 = Array1::<f64>::zeros(ni) + 2.0;

    let mut i1 = Array1::<usize>::zeros(ni);
    let mut i2 = Array1::<usize>::zeros(ni);

    for i in 1..ni {
        i1[i] = i * 5;
        i2[i] = 400 + i * 5;
    }

    let nj: usize = 1000;
    let nthreads = rayon::current_num_threads();
    let mut sum1 = Vec::<Array1::<f64>>::new();
    let mut sum2 = Vec::<Array1::<f64>>::new();
    for _j in  0..nthreads {
        sum1.push(Array1::<f64>::zeros(nj));
        sum2.push(Array1::<f64>::zeros(nj));
    }
    let x = Array1::<f64>::zeros(nj);

    let ii: Vec<usize> = (0..ni).collect();
    ii.par_iter().for_each(|i| {
        let ith = rayon::current_thread_index().unwrap();
        let mut su1 = &mut sum1[ith];
        let mut su2 = &mut sum2[ith];

        for j in i1[*i]..i2[*i] {
            let tmp = func(x[j], a1[*i], a2[*i], a3[*i]);
            su1[j] += a4[*i] * tmp;
            su2[j] += a5[*i] * tmp;
        }
    });

    (sum1, sum2)
}

fn main() {
    let (_sum1, _sum2) = sum_over();
}
``

You can’t write to data from multiple threads without some form of synchronization or arranging that the accesses don’t overlap. Calling a function to get an index and then using &mut some1[ith] might not overlap, but the compiler can’t understand and prove that, so it doesn’t accept your code.

When using rayon, the solution is generally to use rayon’s operators to collect results rather than trying to write them into shared storage yourself. In this case, you can use fold(). Don’t mention the number of threads at all; part of the point of rayon is that work scheduling is flexible, and your code won't necessarily run on all threads if there is other simultaneous work.

let (sum1, sum2) = (0..ni)
    .into_par_iter()
    .fold(
        || (Array1::<f64>::zeros(nj), Array1::<f64>::zeros(nj)),
        |(mut sum1, mut sum2), i| {
            for j in i1[i]..i2[i] {
                let tmp = func(x[j], a1[i], a2[i], a3[i]);
                sum1[j] += a4[i] * tmp;
                sum2[j] += a5[i] * tmp;
            }
            (sum1, sum2)
        },
    )
    .unzip();
3 Likes

Thank you very much. This now works for me.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.