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-05-10
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.
Cross-references: rust-async-trait-sync-bound, message-passing-vs-shared-memory.