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: Sendmeans ownership of a value of typeTcan be safely transferred (moved) to another thread.T: Syncmeans a value of typeTcan be safely referenced from multiple threads concurrently; by definition,T: Syncif 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>`