Map

Rust Send and Sync

Wiki conceptrusttype-systemconcurrency โ†ณ show in map Markdown
title
Rust Send and Sync
type
concept
summary
The two auto-traits that govern thread safety in Rust; Send for cross-thread move, Sync for cross-thread shared reference, with &T: Send โ‡” T: Sync
tags
rust, type-system, concurrency
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 of T can be moved to another thread. Rc<T> is not Send (refcount race); Arc<T> is Send if T: Send + Sync.
  • T: Sync โ€” &T can be shared across threads. Equivalently, T: Sync โ‡” &T: Send. Cell<T> is not Sync (interior mutation without synchronization); Mutex<T>: Sync only requires T: 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>: Sync only requires T: Send
  • Use &mut self instead of &self โ€” &mut T: Send only needs T: 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.

Sub-pages