Send and Sync
Rust uses two marker traits to decide what may cross between threads. You almost never write them yourself. The compiler works them out from the types inside.
Send
A type is Send if a value of it can move to another thread. String, Vec<T>, numbers and Arc<T> are all Send. thread::spawn needs its closure, and everything it captures, to be Send. A generic function that spawns a thread with a T writes T: Send + 'static: the value must be Send and must not hold short-lived borrows.
A struct is Send when all its fields are Send.
Sync
A type is Sync if a &T can be shared between threads. Put another way, T is Sync when &T is Send.
i32, String and Mutex<T> are Sync. RefCell<T> is Send but not Sync, because it changes its value through a shared reference without any lock.
let c = RefCell::new(0);thread::scope(|s| { s.spawn(|| *c.borrow_mut() += 1); // E0277});Rc is not Send
Rc changes its count without any thread safety. Two threads cloning it at once could break the count. So moving an Rc into a thread is error E0277:
let a = Rc::new(1);thread::spawn(move || *a + 1); // E0277The fix is to use Arc instead. Rc is not Sync either. An Rc made and used only inside one thread is fine, but handing it back from the thread is E0277 too.
Atomics
Atomic types like AtomicUsize change a number safely from many threads, without a Mutex. They are Sync, so threads can share them by reference.
use std::sync::atomic::{AtomicUsize, Ordering};let c = AtomicUsize::new(5);let old = c.fetch_add(1, Ordering::SeqCst);// old is 5, c is now 6fetch_add returns the old value. load reads the current value.
Remember
Send: can move to another thread.Sync:&Tcan be shared between threads.- E0277 with
Rcinthread::spawnmeans "useArc". - Atomics are a fast, lock-free choice for simple counters.