Вы на самом деле ответили на свой вопрос - все замыкания, которые должны быть разделены между потоками, должны быть Sync
, а API Rayon просто требует, чтобы они были Sync
через границы черты.См., Например, документацию ParallelIterator::map()
, в которой метод обозначен как
fn map<F, R>(self, map_op: F) -> Map<Self, F> where
F: Fn(Self::Item) -> R + Sync + Send,
R: Send,
Здесь нет более глубокой магии - всякий раз, когда Rayon использует замыкание так, как этого требуетравным Sync
, например, передав его API-интерфейсу более низкого уровня, Rayon ограничивает соответствующий тип параметра границей Sync
.Это, в свою очередь, гарантирует, что все, что хранится внутри замыкания, является Sync
, поэтому в замыкании нельзя хранить RefCell
.
В подобных случаях вы также можете запросить объяснение у компилятора.,Например, если вы попытаетесь скомпилировать этот код
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();
}
, вы получите эту ошибку ( детская площадка )
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>]`
Хотя компилятор, к сожалению, не работает напрямуюупомяните черту, привязанную к параметру map()
, он все еще указывает вам на соответствующий метод и объясняет, что ожидает закрытие равным Sync
, и причину, по которой это не так.