Why doesn't rayon need arc

let mut output =Mutex::new(
        vec![vec![vec![255u8, 24u8, 157u8]; ini.3[1] as usize]; ini.3[0] as usize]);
               ...code...
               //ignore the fact that it is a 3 dimensional vector. It won't matter in this case perfomance wise.
    let mut rayon_needs_a_vec_for_par_iter=Vec::with_capacity(ini.3[0] as usize+1);
    for x in 0..ini.3[0] as usize{
        rayon_needs_a_vec_for_par_iter.push(x)
    }
    rayon_needs_a_vec_for_par_iter.par_iter().for_each (|i|{
        for j in 0..ini.3[1] as usize {
           ...code...
            output.lock().unwrap()[(*i)][j] =some_value_after_computation(*i,j)
        }
    });

This code compiles and works perfectly. Why didn't rayon need Arc Mutex for output variable. How come is it able to work with output just being Mutex.

What does rayon do with the closure internally that it is able to use this output variable in multiple threads without Arc.

How come we can have multiple some_value_after_computation(*i,j) running at the same time.

The closure passed to the for_each is able to borrow the Mutex without requiring the 'static bound:

fn for_each<OP>(self, op: OP)
where
    OP: Fn(Self::Item) + Sync + Send,

Compare with the bounds required for the closure in std::thread::spawn:

pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where
    F: FnOnce() -> T,
    F: Send + 'static, // <- F is bounded by the 'static lifetime
    T: Send + 'static,

That’s why you can use &Mutex<T> instead of Arc<Mutex<T>>.

std::thread::scope is not requiring the closure to be bounded by 'static either.

A shared reference (denoted by &) can be copied and shared.

5 Likes