Theme
Q045. What do Send and Sync guarantee?
Short Answer
Send means ownership of a value can be transferred safely to another thread. Sync means shared references to a value can be used safely from multiple threads; equivalently, T is Sync when &T is Send. They are unsafe auto traits: the compiler derives them from a type's fields unless a component opts out. They prevent data races at thread boundaries but do not guarantee higher-level correctness or freedom from deadlocks.
Deep Dive
Most ordinary value types are Send and Sync. Rc<T> is neither because its reference count is non-atomic. RefCell<T> can be Send when T: Send, but it is not Sync because its runtime borrow counter cannot be accessed concurrently. Mutex<T> is designed to make synchronized shared access possible and is Sync when its protected T can be sent between threads.
The traits have no methods and usually no runtime cost. APIs such as thread::spawn and tokio::spawn encode them as bounds. Incorrect manual implementations can make safe code data-race, so unsafe impl Send or Sync requires a full synchronization argument.
Internal Model
The compiler computes auto-trait implementations structurally and checks bounds during compilation. Raw pointers and marker fields can influence automatic results. At runtime there is no Send or Sync flag.
These traits establish whether cross-thread access is permitted; locks and atomics establish how particular mutations are coordinated.
Example
rust
use std::sync::{Arc, Mutex};
use std::thread;
fn assert_send_sync<T: Send + Sync>() {}
fn main() {
assert_send_sync::<Arc<Mutex<Vec<u64>>>>();
let values = Arc::new(Mutex::new(vec![1]));
let worker_values = Arc::clone(&values);
thread::spawn(move || worker_values.lock().unwrap().push(2))
.join()
.unwrap();
assert_eq!(*values.lock().unwrap(), vec![1, 2]);
}Common Mistakes
- Defining
Syncas permission to moveTto another thread. - Assuming
Send + Syncmakes every multi-step operation logically atomic. - Writing manual unsafe implementations merely to satisfy a compiler error.
Follow-ups
- Why is
Rc<T>neitherSendnorSync? - Why is
Mutex<T>able to beSync? - How can a local variable make an async future non-
Send?
Related
- Q041. Why does
Arcnot automatically makeTthread-safe? - Q044. How does an OS thread differ from an async task?
- Q064. What makes a future
Send, and why doestokio::spawnrequireSend + 'static?
References
- Rustonomicon
- Rust Standard Library
- The Rust Programming Language