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.

3 minfearless concurrencycannot be sent between threads safelythread safetymarker traits

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, T is Sync when &T is Send.

Almost everything is both. The exceptions are types whose internal mutation isn't synchronized:

TypeSendSyncWhy
Rc<T>NoNoIts reference count is a plain integer; two threads updating it would race
Cell<T>, RefCell<T>Yes (if T is)NoMutation 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 pointersNoNoThe 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:

text
error[E0277]: `Rc<Vec<i32>>` cannot be sent between threads safely

Swap 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.

Learn it in depth