Concept · Rust
Send and Sync
Two auto traits that tell the compiler which types may move to or be shared with other threads — the reason safe Rust has no data races.
Send and Sync are marker traits: they have no methods, and the compiler implements them automatically for a type when all of its fields have them.
Send— a value of the type can be moved to another thread.Sync— a value can be shared between threads by reference. Formally,TisSyncwhen&TisSend.
Almost everything is both. The exceptions are types whose internal mutation isn't synchronized:
| Type | Send | Sync | Why |
|---|---|---|---|
Rc<T> | No | No | Its reference count is a plain integer; two threads updating it would race |
Cell<T>, RefCell<T> | Yes (if T is) | No | Mutation through &T without synchronization |
Arc<T> | Yes (if T: Send + Sync) | Yes (same) | Atomic reference count |
Mutex<T> | Yes (if T: Send) | Yes (if T: Send) | Serializes access |
| Raw pointers | No | No | The compiler can't reason about them |
Where they're checked
std::thread::spawn and tokio::spawn require the closure or future — and everything it captures or holds across an .await — to be Send:
error[E0277]: `Rc<Vec<i32>>` cannot be sent between threads safelySwap Rc for Arc, and RefCell for Mutex, and it compiles.
Why this ends data races
A data race needs shared memory plus unsynchronized writes. The borrow checker forbids unsynchronized writes through ordinary references; Send and Sync forbid sharing types that mutate internally without synchronization. Together they make data races a compile error in safe Rust. They don't prevent deadlocks or logic races, which you still design against.
Implementing them by hand (unsafe impl Send for T {}) is an unsafe promise, needed only for types built on raw pointers. See the concurrency lesson.