Send and Sync: The Traits Behind Fearless Concurrency

How does the Rust compiler actually know that an `Arc<Mutex<T>>` is safe to share across threads but an `Rc<T>` isn't? The answer is two of the most elegant ideas in Rust: a pair of marker traits, `Send` and `Sync`, that encode thread-safety directly into the type system. They're rarely written by hand and often invisible, yet they're the machinery that makes fearless concurrency work — the compiler reasons about thread-safety by checking these traits, automatically, at compile time.

This post reveals the machinery behind Rust’s concurrency guarantees: the Send and Sync marker traits. They encode, in the type system, which types can be safely moved to another thread (Send) and shared between threads (Sync). The compiler uses them to check thread-safety automatically — this is how Rust catches data races at compile time. You rarely implement them yourself, but understanding them explains why the previous posts’ rules hold.

What Send and Sync are

Send and Sync are marker traits — traits with no methods, that simply mark a type as having a property. They mark thread-safety:

Send (safe to move to another thread) and Sync (safe to share by reference between threads) are marker traits — no methods, just encoding thread-safety properties into the type system so the compiler can reason about them. They’re the vocabulary in which Rust expresses thread-safety. And remarkably, the compiler assigns them automatically.

They’re automatic (auto traits)

The elegant part: Send and Sync are auto traits — the compiler automatically implements them for types whose components are all Send/Sync, so you rarely deal with them explicitly:

Send and Sync are auto traits — automatically implemented by the compiler for types whose components all have them — so most types get thread-safety automatically (why concurrency “just works” for safe types), while non-thread-safe types (like Rc, whose counting isn’t atomic) don’t get them, so the compiler automatically rejects unsafe cross-thread use. This automatic mechanism is how the compiler enforces thread-safety.

How the compiler uses them

Send and Sync are the mechanism behind Rust’s compile-time concurrency safety — the compiler checks them to enforce thread-safety:

The compiler enforces thread-safety by checking Send (required to move data to a thread) and Sync (required to share data across threads) — rejecting types that lack them (like Rc). Send/Sync are the machinery behind Rust’s compile-time data-race prevention: the guarantees of fearless concurrency are implemented as trait bounds the compiler checks. This is elegant, but it’s also why you rarely touch them directly.

What this means in practice

For everyday Rust, Send/Sync mostly work invisibly — but understanding them clarifies the concurrency model, and there are a few practical points:

Send and Sync are the marker traits — automatic and mostly invisible — that encode thread-safety in Rust’s type system: Send (safe to move to a thread), Sync (safe to share between threads). The compiler checks them to enforce data-race-free concurrency at compile time, which is the machinery behind fearless concurrency. You rarely implement them, but understanding them explains why the guarantees hold. Next: message passing — the other concurrency paradigm, using channels.

Key takeaways

Further reading

Sources & References