Course contentsShow
← Rust
Lesson 1036 of 1,9109. Smart Pointers and Interior MutabilityFree lesson

Memory Safety Guarantees for Concurrent Data

Verify thread safety using type-level markers like Sync and Send.

In this lesson, you will learn how Rust uses the Send and Sync marker traits to enforce thread safety at the type level.

When working with concurrent data structures—like those using Arc<Mutex<T>> or Atomic types—the Rust compiler must guarantee that data races cannot occur. It does this by checking whether your types are safe to pass between threads or access from multiple threads simultaneously.

The Role of Marker Traits

These checks are handled by two fundamental marker traits defined in the standard library:

  • Send: A type is Send if it is safe to transfer ownership of it to another thread. Most Rust types are Send, but types that hold non-thread-safe resources (like Rc<T>, which uses a non-atomic reference counter) are not.
  • Sync: A type is Sync if it is safe to share between threads via shared references (&T). A type T is Sync if and only if &T is Send.

Enforcing Safety at Compile Time

Consider an Rc<T>. Because Rc updates its reference count without atomic operations, it is not thread-safe. If you try to move an Rc into a new thread, the compiler checks the Send trait. Since Rc explicitly opts out of Send, the compiler will throw an error, preventing a potential data race on the reference count.

When you use Arc<T>, you are using a version of reference counting that satisfies the Send and Sync requirements because it uses atomic operations for its counter.

use std::sync::Arc;
use std::rc::Rc;
use std::thread;

fn main() {
    let shared = Arc::new(5); // Arc is Send + Sync
    thread::spawn(move || {
        println!("{:?}", shared);
    });

    // let local = Rc::new(5);
    // thread::spawn(move || { println!("{:?}", local); }); 
    // ^ Error: Rc<i32> cannot be sent between threads safely
}

The beauty of this system is that it is entirely type-driven. You don't need to manually label your structs; the compiler automatically derives Send and Sync based on the fields of your type. If all fields are Send or Sync, your struct is too. If you include an unsafe component, you can manually implement these traits only after verifying the logic is sound.

Key Takeaway: The Send and Sync marker traits are the compiler's primary mechanism for ensuring that concurrent data access remains memory-safe by blocking non-thread-safe types from crossing thread boundaries.