Course contentsShow
← Rust
Lesson 1133 of 1,91010. Concurrency PrimitivesFree lesson

The Sync Trait and Shared Reference Safety

You will learn how the compiler enforces that shared access to T is safe only if &T is thread-safe.

In this lesson, you will learn how the Sync marker trait allows the Rust compiler to guarantee that shared access to data is thread-safe.

What is Sync?

If the Send trait tells the compiler that a type can be moved to another thread, the Sync trait tells the compiler that a type can be safely shared between threads. Specifically, a type T is Sync if and only if a shared reference &T is Send.

In Rust’s memory model, &T represents an immutable, shared reference. If T is Sync, it means that multiple threads can hold &T simultaneously without causing data races. If the compiler cannot prove that multiple threads reading &T will not result in undefined behavior, it will refuse to compile your code.

The Logic of Shared Access

Why does Sync require &T to be Send? Because if you can safely pass a reference &T across threads, those threads can then concurrently read that data. If T contains "interior mutability" (like Cell or RefCell), allowing multiple threads to share &T would be catastrophic, as they could mutate the same memory location simultaneously without synchronization.

Consider this example:

use std::rc::Rc;

// This will fail to compile!
// Rc is not Sync because it uses non-atomic reference counting.
// If we could share &Rc across threads, two threads might
// increment/decrement the count simultaneously, causing a data race.
fn is_sync<T: Sync>() {}

fn main() {
    is_sync::<Rc<i32>>(); // Error: Rc<i32> does not implement Sync
}

Because Rc uses a standard, non-atomic counter, sharing it across threads could lead to corrupted memory. Rust prevents this by simply not marking Rc as Sync. Conversely, Arc (Atomic Reference Counter) is Sync, because it uses CPU-level atomic operations to manage the reference count, ensuring that concurrent updates remain consistent and safe.

The compiler acts as a gatekeeper, checking these trait bounds at compile time. If you try to use a type that isn't Sync in a multi-threaded context (like passing it into a thread::spawn closure via a reference), the compiler will catch it before you ever run your code.

Key Takeaway: The Sync trait is the compiler's way of ensuring that shared references (&T) are safe to access across multiple threads, preventing data races by restricting non-thread-safe types from being shared.