Essential Standard Traits

Send and Sync

The Send and Sync traits in std::marker are unsafe auto traits that enforce compile-time thread safety, preventing data races across threads.

Definitions of Send and Sync

pub unsafe auto trait Send {}
pub unsafe auto trait Sync {}
  • T: Send means ownership of a value of type T can be safely transferred (moved) to another thread.
  • T: Sync means a value of type T can be safely referenced from multiple threads concurrently; by definition, T: Sync if and only if &T: Send.

Because both are auto traits, the compiler implements Send and Sync automatically for any struct or enum whose fields are all Send or Sync.

Thread-Safety Matrix of Common Types

Type Send? Sync? Reason
Primitives, String, Vec<T>, Box<T> (for T: Send + Sync) Yes Yes Exclusive ownership with no unsynchronized shared state
Rc<T> No No Uses non-atomic reference counting; concurrent cloning/dropping causes data races
Arc<T> (where T: Send + Sync) Yes Yes Uses atomic operations (AtomicUsize) for reference counting
Cell<T> / RefCell<T> (for T: Send) Yes No Can be moved to another thread, but unsynchronized interior mutability forbids shared & access across threads
Mutex<T> (where T: Send) Yes Yes Serializes access with an OS/futex lock, making &Mutex<T> safe to share across threads
Raw pointers (*const T, *mut T) No No Deliberately opt out of automatic thread safety so wrappers must justify safety explicitly

Sharing State Across Threads with Arc<Mutex<T>>

Because std::thread::spawn requires the closure and its captured values to be Send + 'static, sharing mutable state across threads combines Arc<T> (shared ownership) with Mutex<T> (synchronized interior mutability):

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

fn main() {
    let total = Arc::new(Mutex::new(0_u32));
    let mut handles = Vec::new();

    for _ in 0..4 {
        let shared = Arc::clone(&total);
        handles.push(thread::spawn(move || {
            let mut guard = shared.lock().unwrap();
            *guard += 25;
        }));
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Final total: {}", *total.lock().unwrap());
}
Final total: 100

Compile-Time Rejection of Non-Send Types (E0277)

Attempting to move an Rc<T> into a spawned thread fails at compile time with E0277:

// ❌ does not compile
use std::rc::Rc;
use std::thread;

fn main() {
    let rc = Rc::new(42);
    thread::spawn(move || {
        println!("{rc}");
    });
}
error[E0277]: `Rc<i32>` cannot be sent between threads safely
 --> src/main.rs:7:19
  |
7 |       thread::spawn(move || {
  |       ------------- ^------
  |       |             |
  |  _____|_____________within this `{closure@src/main.rs:7:19: 7:26}`
  | |     |
  | |     required by a bound introduced by this call
8 | |         println!("{rc}");
9 | |     });
  | |_____^ `Rc<i32>` cannot be sent between threads safely
  |
  = help: within `{closure@src/main.rs:7:19: 7:26}`, the trait `Send` is not implemented for `Rc<i32>`