use std::thread;
#[derive(Debug)]
struct Thing;
// Can send between threads!
fn main() {
let thing = Thing;
thread::spawn(move || {
println!("{:?}", thing);
}).join().unwrap();
}
There are two special traits in Rust for concurrency semantics.
Send
marks a structure safe to send between threads.
Sync
marks a structure safe to share between threads.
(&T
is Send
)
These traits are what Rust uses to prevent data races.
They are automatically derived for all types if appropriate.
use std::thread;
#[derive(Debug)]
struct Thing;
// Can send between threads!
fn main() {
let thing = Thing;
thread::spawn(move || {
println!("{:?}", thing);
}).join().unwrap();
}
There are some notable types which are not Send
or Sync
.
Such as Rc
, raw pointers, and UnsafeCell
.
Rc
use std::rc::Rc;
use std::thread;
// Does not work!
fn main() {
let only_one_thread = Rc::new(true);
thread::spawn(move || {
println!("{:?}", only_one_thread);
}).join().unwrap();
}
Rc
error[E0277]: the trait bound `std::rc::Rc<bool>: std::marker::Send` is not satisfied
--> <anon>:7:5
|
7 | thread::spawn(move || {
| ^^^^^^^^^^^^^ the trait `std::marker::Send` is not implemented for `std::rc::Rc<bool>`
It’s possible to add the implementation of Send
and Sync
to a type.
struct Thing(*mut String);
unsafe impl Send for Thing {}
unsafe impl Sync for Thing {}
In these cases, the task of thread safety is left to the implementor.
If a type implements both Sync
and Copy
then it can also implement Send
.
A type &T
can implement Send
if the type T
also implements Sync
.
unsafe impl<'a, T: Sync + ?Sized> Send for &'a T {}
A type &mut T
can implement Send
if the type T
also implements Send
.
unsafe impl<'a, T: Send + ?Sized> Send for &'a mut T {}
What are the consequences of having Send
and Sync
?
Carrying this information at the type system level allows driving data race bugs down to a compile time level.
Preventing this error class from reaching production systems.
Send
and Sync
are independent of the choice of concurrency (async, threaded, etc.).