Skip to content

Q045. What do Send and Sync guarantee?

Short Answer

Send means ownership of a value can be transferred safely to another thread. Sync means shared references to a value can be used safely from multiple threads; equivalently, T is Sync when &T is Send. They are unsafe auto traits: the compiler derives them from a type's fields unless a component opts out. They prevent data races at thread boundaries but do not guarantee higher-level correctness or freedom from deadlocks.

Deep Dive

Most ordinary value types are Send and Sync. Rc<T> is neither because its reference count is non-atomic. RefCell<T> can be Send when T: Send, but it is not Sync because its runtime borrow counter cannot be accessed concurrently. Mutex<T> is designed to make synchronized shared access possible and is Sync when its protected T can be sent between threads.

The traits have no methods and usually no runtime cost. APIs such as thread::spawn and tokio::spawn encode them as bounds. Incorrect manual implementations can make safe code data-race, so unsafe impl Send or Sync requires a full synchronization argument.

Internal Model

The compiler computes auto-trait implementations structurally and checks bounds during compilation. Raw pointers and marker fields can influence automatic results. At runtime there is no Send or Sync flag.

These traits establish whether cross-thread access is permitted; locks and atomics establish how particular mutations are coordinated.

Example

rust
use std::sync::{Arc, Mutex};
use std::thread;

fn assert_send_sync<T: Send + Sync>() {}

fn main() {
    assert_send_sync::<Arc<Mutex<Vec<u64>>>>();

    let values = Arc::new(Mutex::new(vec![1]));
    let worker_values = Arc::clone(&values);
    thread::spawn(move || worker_values.lock().unwrap().push(2))
        .join()
        .unwrap();

    assert_eq!(*values.lock().unwrap(), vec![1, 2]);
}

Common Mistakes

  • Defining Sync as permission to move T to another thread.
  • Assuming Send + Sync makes every multi-step operation logically atomic.
  • Writing manual unsafe implementations merely to satisfy a compiler error.

Follow-ups

  • Why is Rc<T> neither Send nor Sync?
  • Why is Mutex<T> able to be Sync?
  • How can a local variable make an async future non-Send?

References

  • Rustonomicon
  • Rust Standard Library
  • The Rust Programming Language