Shared Mutable State (The Rust Way)

Why Rust forces you to choose between sharing and mutating.

The idea

Every data race in history requires exactly two ingredients: Sharing (two threads can access the data) and Mutation (at least one thread changes it). In C++ or Java, it's easy to accidentally share mutable data, relying on developers to remember to add locks. Rust's compiler literally makes it illegal to compile code that has both Sharing and Mutation without explicit, safe wrappers.

Step 1: The Golden Rule of Rust: You can have infinitely many readers, OR exactly one writer, but never both.

How it works (Arc and Mutex)

If you want to share data across threads in Rust, you wrap it in an Arc (Atomic Reference Count). This gives you Shared Immutable state. If you also need to modify it, you must wrap the data in a Mutex inside that Arc. The compiler forces you to acquire the lock before accessing the data. If you don't, it will not compile.

// RUST EXAMPLE

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

// 1. Mutex gives us Mutability (safe mutation).
// 2. Arc gives us Sharing (safe cross-thread references).
let counter = Arc::new(Mutex::new(0));

let mut handles = vec![];

for _ in 0..10 {
    // Clone the Arc to get a reference for the new thread
    let counter_ref = Arc::clone(&counter);
    
    let handle = thread::spawn(move || {
        // COMPILER ERROR if you try to mutate counter_ref directly here!
        
        // You MUST acquire the lock to get the mutable data inside
        let mut num = counter_ref.lock().unwrap();
        *num += 1;
    });
    handles.push(handle);
}

Cost

The cost is developer friction. Rust's "Fearless Concurrency" means you will spend time fighting the borrow checker to prove your code is safe. The benefit is that if your multi-threaded Rust code compiles, it is mathematically guaranteed to be free of Data Races.

Watch out for