Как это работает?
Давайте проверим требования thread::spawn
еще раз:
pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where
F: FnOnce() -> T,
F: Send + 'static, // <-- this line is important for us
T: Send + 'static,
Поскольку Foo
содержит Arc<Receiver<_>>
, давайте проверим, как и как Arc
реализует Send
:
impl<T> Send for Arc<T>
where
T: Send + Sync + ?Sized,
Итак, Arc<T>
реализует Send
, если T
реализует Send
и Sync
. И хотя Receiver
реализует Send
, он не реализует Sync
.
Так почему Arc
предъявляет такие строгие требования к T
? T
также должен реализовывать Send
, потому что Arc
может действовать как контейнер; если бы вы могли просто спрятать что-то, что не реализует Send
в Arc
, отправьте это в другой поток и распакуйте его там ... могут случиться плохие вещи. Интересная часть состоит в том, чтобы понять, почему T
также должен реализовывать Sync
, что, очевидно, также является частью, с которой вы боретесь:
Ошибка не имеет смысла для меня, поскольку я не пытаюсь разделить ее между потоками (я перемещаю ее, а не клонирую).
Компилятор не может знать, что Arc
в Foo
фактически не является общим. Подумайте, хотите ли вы добавить #[derive(Clone)]
к Foo
позже (что возможно без проблем):
fn main() {
let (example, sender) = Foo::new();
let clone = example.clone();
let handle = example.run_thread();
clone.run();
// oopsie, now the same `Receiver` is used from two threads!
handle.join();
}
В приведенном выше примере есть только один Receiver
, который используется несколькими потоками. И это не хорошо, так как Receiver
не реализует Sync
!
Для меня этот код поднимает вопрос: почему Arc
на первом месте? Как вы заметили, без Arc
он работает без проблем: вы четко заявляете, что Foo
является единственным владельцем Receiver
. И если вы «не пытаетесь поделиться [Получателем]» в любом случае, нет смысла иметь несколько владельцев.