Rust Send and Sync
- title
- Rust Send and Sync
- type
- concept
- summary
- The two auto-traits that govern thread safety in Rust;
Sendfor cross-thread move,Syncfor cross-thread shared reference, with&T: SendβT: Sync - tags
- rust, type-system, concurrency
- sources
- verrchu-sync-bound
- created
- 2026-05-10
- updated
- 2026-07-29
Send and Sync are Rust's two auto-traits for thread safety. They're auto-derived (every struct gets them iff all fields have them), and they govern what the borrow checker permits across threads:
T: Sendβ values ofTcan be moved to another thread.Rc<T>is notSend(refcount race);Arc<T>isSendifT: Send + Sync.T: Syncβ&Tcan be shared across threads. Equivalently,T: Syncβ&T: Send.Cell<T>is notSync(interior mutation without synchronization);Mutex<T>: Synconly requiresT: Send.
The non-obvious rules that make this load-bearing in practice:
| Type | Send requires |
Sync requires |
|---|---|---|
&T |
T: Sync |
T: Sync |
&mut T |
T: Send |
T: Sync |
Box<T> |
T: Send |
T: Sync |
Rc<T> |
never | never |
Arc<T> |
T: Send + Sync |
T: Send + Sync |
Mutex<T> |
T: Send |
T: Send |
RwLock<T> |
T: Send |
T: Send + Sync |
Cell<T> |
T: Send |
never |
The &T: Send β T: Sync rule is the one that surprises people, and it's the source of the rust-async-trait-sync-bound gotcha β capturing &self in a Send future silently demands Self: Sync, even when nothing in the code obviously asks for it.
Workarounds when you have a !Sync field but need a Send future:
- Wrap the field in
Mutex<T>βMutex<T>: Synconly requiresT: Send - Use
&mut selfinstead of&selfβ&mut T: Sendonly needsT: Send - Make the captured value owned (move into the future) β owned-vs-borrowed avoids the issue
For the language-design rationale (&mut as uniqueness, not mutation), Niko Matsakis's "Focusing on ownership" is canonical. safety-in-an-unsafe-world uses Send as its worked example of a library-defined safety property: the trait's meaning lives entirely in its doc comment, unsafe impl is the enforcement, and the F: Send bound on spawn is what justifies the pthread_create call underneath.
Cross-references: rust-async-trait-sync-bound, message-passing-vs-shared-memory.