Send and Sync
Concept
source: https://doc.rust-lang.org/nomicon/send-and-sync.html
-
Definition:
- Something is
Sendif it either- Does not share mutable state with something else, or
- Ensures exclusive access to any shared mutable state.
- a type T is
Syncif and only if&TisSend.
- Something is
-
Automatic Implementation:
- Most types in Rust are automatically
Send. - Primitive types (like
i32,bool, etc.) areSend. - Most standard library types are
Send. - Structs and enums are automatically
Sendif all their fields areSend.
- Most types in Rust are automatically
-
Major exceptions
- raw pointers are neither
SendnorSync(because they have no safety guards).- raw pointers are, strictly speaking, marked as thread-unsafe as more of a
lint. If you ensure your struct containing raw pointers is thread-safe,
you can mark your struct
SendandSync.
- raw pointers are, strictly speaking, marked as thread-unsafe as more of a
lint. If you ensure your struct containing raw pointers is thread-safe,
you can mark your struct
UnsafeCellisn’tSync(and thereforeCellandRefCellaren’t).Rcisn’tSendorSync(because the refcount is shared and unsynchronized).RcandUnsafeCellare very fundamentally not thread-safe: they enable unsynchronized shared mutable state
- raw pointers are neither
Usage of Send and Sync
Not Send or Sync
pub(crate) struct NotSendOrSync(#[allow(dead_code)] *mut ());Send but not Sync
Here’s an example of a type that is Send but not Sync:
use std::cell::Cell;
// SendButNotSync automatically Send but not Sync,
// because Cell<T> is Send but not Sync
#[derive(Debug)]
struct SendButNotSync {
value: Cell<i32>,
}
impl SendButNotSync {
fn new(initial: i32) -> Self {
SendButNotSync {
value: Cell::new(initial),
}
}
fn get(&self) -> i32 {
self.value.get()
}
fn set(&self, new_value: i32) {
self.value.set(new_value);
}
}
fn main() {
let x = SendButNotSync::new(5);
std::thread::spawn(move || {
println!("Value in new thread: {}", x.get());
x.set(10);
}).join().unwrap();
}Send and Sync
Now, let’s demonstrate a type that is both Send and Sync:
// SendAndSync is automatically both Send and Sync
// because AtomicI32 is both Send and Sync
struct SendAndSync {
value: std::sync::atomic::AtomicI32,
}
impl SendAndSync {
fn new(initial: i32) -> Self {
SendAndSync {
value: std::sync::atomic::AtomicI32::new(initial),
}
}
fn get(&self) -> i32 {
self.value.load(std::sync::atomic::Ordering::Relaxed)
}
fn set(&self, new_value: i32) {
self.value.store(new_value, std::sync::atomic::Ordering::Relaxed);
}
}
fn main() {
let x = std::sync::Arc::new(SendAndSync::new(5));
// This is okay because SendAndSync is Send
// let x_clone = &x; // x can not live long enough
let x_clone = x.clone();
std::thread::spawn(move || {
println!("Value in new thread: {}", x_clone.get());
x_clone.set(10);
}).join().unwrap();
println!("Final value in main thread: {}", x.get());
}