Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sync and Send trait when using rayon parallel iterators

I have a collection of my trait, and I'd like to be able to call a mutable method of the trait for each of the items in my map.

At the moment I do this sequentially and my collection looks like the following:

use std::cell::RefCell;
use std::collections::*;
use std::rc::Rc;

trait Trait {
    fn foo_mut(&mut self);
}

fn main() {
    let mut items: HashMap<i32, Rc<RefCell<dyn Trait>>> = HashMap::new();
    // I have a separate data structure that holds Week<RefCell<dyn Trait>>

    for item in items.values_mut() {
        item.borrow_mut().foo_mut();
    }
}

I'd like to call the trait method in parallel now, so I first changed my data structure to:

use std::collections::*;
use std::sync::{Arc, RwLock};

fn main() {
    let mut items: HashMap<i32, Arc<RwLock<dyn Trait>>> = HashMap::new();

    for item in items.values_mut() {
        item.write().unwrap().foo_mut();
    }
}

then I came across rayon, and I tried to use its parallel iterators, but the following code raises an error:

items.par_iter_mut().for_each(|(id, item)| item.write().unwrap().foo_mut());
error[E0599]: no method named `par_iter_mut` found for struct `std::collections::HashMap<i32, std::sync::Arc<std::sync::RwLock<dyn Trait>>>` in the current scope
   --> src/main.rs:12:11
    |
12  |       items.par_iter_mut().for_each(|(id, item)| item.write().unwrap().foo_mut());
    |             ^^^^^^^^^^^^ help: there is an associated function with a similar name: `iter_mut`
    |
    = note: the method `par_iter_mut` exists but the following trait bounds were not satisfied:
            `&mut std::collections::HashMap<i32, std::sync::Arc<std::sync::RwLock<dyn Trait>>>: rayon::iter::IntoParallelIterator`
            which is required by `std::collections::HashMap<i32, std::sync::Arc<std::sync::RwLock<dyn Trait>>>: rayon::iter::IntoParallelRefMutIterator`

I checked the for_each documentation, and it requires Self::Item to be Send and the closure to be Send + Sync, now from what I can see Arc is already Send + Sync, but the code can be fixed by adding both these traits to mine like:

let mut items: HashMap<i32, Arc<RwLock<dyn Trait + Send + Sync>>> = HashMap::new();

Why is this necessary?

like image 249
Nick Avatar asked Apr 30 '26 04:04

Nick


1 Answers

The implementations of Send and Sync for Arc<T> look like this:

impl<T> Send for Arc<T>
where
    T: Send + Sync + ?Sized, 

impl<T> Sync for Arc<T>
where
    T: Send + Sync + ?Sized, 

This means that Arc<T> is only Send and Sync if T is too (See here for an explanation of why).

Likewise, RwLock<T> is only Send and Sync if T is:

impl<T: ?Sized + Send> Send for RwLock<T>
impl<T: ?Sized + Send + Sync> Sync for RwLock<T>

Together, this means that Arc<RwLock<dyn Trait>> will only be Send and Sync if dyn Trait is too. If it is cumbersome to write dyn Trait + Send + Sync and you know that you never want to implement Trait for any types aren't Send or Sync, then you can add these as bounds to the trait:

trait Trait: Send + Sync {
    fn foo_mut(&mut self);
}

Then your original code with Arc<RwLock<dyn Trait>> will work with rayon.

like image 183
Brent Kerby Avatar answered May 02 '26 03:05

Brent Kerby