Fearless Concurrency

Threads, join handles, Arc<Mutex<T>> and channels — with data races ruled out at compile time.

Concurrency bugs — data races, shared state changed by two threads at once — are among the hardest bugs to find. Rust's ownership rules extend to threads: the compiler only lets data cross into another thread if that is safe, and only lets threads share mutable data through synchronization types. The result is often called 'fearless concurrency'.

Three patterns

thread::spawn(move || ...) gives the closure ownership of id; join waits and returns the thread's result.

Arc shares the counter between threads; Mutex makes each increment exclusive. 8 x 1000 always gives exactly 8000.

A channel passes messages: three producer threads send readings, the main thread receives them all.

drop(tx) closes the original sender, so rx.iter() ends once every producer is done. Arrival order varies between runs, so the results are sorted before printing.

Send and Sync

let mut count = 0; thread::spawn(|| count += 1); // does not compile let count = Arc::new(Mutex::new(0)); let c = Arc::clone(&count); thread::spawn(move || *c.lock().unwrap() += 1);

A thread may outlive the variable it borrows, and unsynchronized writes would race. Share ownership with Arc and serialize access with a Mutex (or use an atomic integer).

Why is the counter always exactly 8000?

Each increment happens while holding the Mutex lock, so the read-modify-write of one thread can never interleave with another's.

When would you use a channel instead of Arc<Mutex<T>>?

When threads mainly hand work or results to each other (producer/consumer): message passing avoids shared state entirely ('do not communicate by sharing memory; share memory by communicating').