You actually answered your question yourself – all closures that need to be shared between threads need to be Sync
, and Rayon's API simply requires them to be Sync
via trait bounds. See for example the documentation of ParallelIterator::map()
, which specifies the method as
fn map<F, R>(self, map_op: F) -> Map<Self, F> where
F: Fn(Self::Item) -> R + Sync + Send,
R: Send,
There isn't any deeper magic here – whenever Rayon uses a closure in a way that requires it to be Sync
, e.g. by passing it on to a lower level API, Rayon restricts the corresponding parameter type with the Sync
trait bound. This in turn makes sure that everything stored inside the closure is Sync
, so you can't store any RefCell
in the closure.
In cases like this, you can also ask the compiler for an explanation. As an example, if you try to compile this code
use std::cell::RefCell;
use rayon::prelude::*;
fn main() {
let c = RefCell::new(5);
let _ = [1, 2, 3]
.par_iter()
.map(|i| i * *c.borrow())
.sum();
}
you will get this error (playground)
error[E0277]: `std::cell::RefCell<i32>` cannot be shared between threads safely
--> src/main.rs:10:10
|
10 | .map(|i| i * *c.borrow())
| ^^^ `std::cell::RefCell<i32>` cannot be shared between threads safely
|
= help: within `[closure@src/main.rs:10:14: 10:33 c:&std::cell::RefCell<i32>]`, the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<i32>`
= note: required because it appears within the type `&std::cell::RefCell<i32>`
= note: required because it appears within the type `[closure@src/main.rs:10:14: 10:33 c:&std::cell::RefCell<i32>]`
While the compiler unfortunately does not directly mention the trait bound for the parameter of map()
, it still points you to the relevant method, and explains that it expects the closure to be Sync
, and the reason why it isn't.