Course contentsShow
← Rust
Lesson 249 of 1,9103. Borrowing and ReferencesFree lesson

The Send and Sync Traits

Understand how trait bounds relate to the thread-safety of borrowed references.

In this lesson, you will learn how the Send and Sync traits act as "safety markers" that tell the Rust compiler whether your data is safe to share across thread boundaries.

The Thread-Safety Contracts

When we talk about concurrency in Rust, the compiler needs to know if a piece of data can be moved to another thread or accessed by multiple threads simultaneously. Because Rust’s borrowing rules are so strict, it uses two special marker traits to enforce thread safety:

  1. Send: A type is Send if it is safe to transfer ownership of it to another thread. Most types in Rust are Send, but things like Rc<T> are not, because their internal reference counting isn't thread-safe.
  2. Sync: A type is Sync if it is safe to access it from multiple threads through shared references (&T). A type is only Sync if &T is Send.

How References Relate to Bounds

You might wonder why we worry about this when using borrowed references. Think of a borrowed reference like &T as a "permission to look."

If you have a function that spawns a thread and tries to move a reference into it, the compiler checks the trait bounds of the data you are passing. If your type is not Sync, you cannot safely create a shared reference (&T) that multiple threads can look at, because multiple threads could try to modify that data simultaneously, violating Rust’s core borrowing guarantees.

Consider this example:

fn process_data<T: Sync>(data: &T) {
    // Because T is Sync, we are guaranteed that 
    // multiple threads can safely hold &T simultaneously.
    std::thread::spawn(move || {
        println!("{:?}", data);
    });
}

If you tried to pass a type that wasn't Sync into this function, the compiler would stop you. It knows that the thread could cause a "data race" if the underlying type isn't designed to handle concurrent access.

By requiring Sync on borrowed references in multi-threaded contexts, Rust ensures that your program remains free of data races at compile time. You don't have to write these traits yourself; the compiler automatically implements them for your types if all their fields are already Send or Sync.

Key Takeaway: Send allows ownership to move between threads, while Sync allows multiple threads to safely hold shared references to the same data.