Send
essentially means that the type can safely be moved into another thread.
Sync
essentially means that the type can be shared as &T
between multiple threads.
Most types are both Send
and Sync
, however there are exceptions.
For instance, Cell<T>
type is Send
(when T
is Send
) because it's safe for Cell
to change thread it is used in - as long it will be accessible from only one thread at the same time. However, it is not Sync
, as it cannot be safely shared between multiple threads due to interior mutability.
Rc<T>
type is not Send
because due to possibility that there are other Rc
instances in the current thread - which would make it so that there are two Rc
s using the same non-atomic counter in different threads. In theory it is possible to send Rc
to another thread if you can prove that this is the only reference - this can be done with get_mut
or make_mut
method of Rc
.
Now for why Sync
in Mutex
requires Send
as opposed to Sync
. This is because Mutex<T>
enforces that the mutex guard can be only obtained from a single thread. For instance, the following code is safe, because Cell
is only accessed from a single thread at the same time, so there is no risk of race conditions.
For understanding purposes in this example, crossbeam::scope
is a block which automatically join
s all threads at end of scope spawned by scope object. It isn't part of Rust standard library, but it makes things a bit easier to understand.
extern crate crossbeam;
use std::sync::Mutex;
use std::cell::Cell;
fn main() {
let mutex = Mutex::new(Cell::new(0));
crossbeam::scope(|scope| for _ in 0..10 {
scope.spawn(|| {
let cell = mutex.lock().unwrap();
cell.set(cell.get() + 1);
});
});
}
Note that Cell<T>
obtained from Mutex<T>
still cannot be shared between multiple threads as it's not a Sync
type. This is an invalid code as it tries to make reference from Mutex<T>
be shared between multiple threads.
extern crate crossbeam;
use std::sync::Mutex;
use std::cell::Cell;
fn main() {
let mutex = Mutex::new(Cell::new(0));
crossbeam::scope(|scope| for _ in 0..10 {
scope.spawn(|| {
let cell = mutex.lock().unwrap();
crossbeam::scope(|inner_scope| {
inner_scope.spawn(|| cell.set(cell.get() + 1));
cell.set(cell.get() + 1)
});
});
});
}
Not so fun fact: In Rust 1.18 and lower, the code I wrote above actually used to be allowed, that was a bug, and is fixed in current versions of Rust as MutexGuard<T>
didn't require T
to be Sync
in order to be itself Sync
. So if you test the above example, you may find it actually working.
(to think that if this question would be asked 2 months ago, I probably would accidentally find this issue while trying to explain why is it so)