SyntaxStudy
Sign Up
Rust Shared State with Arc<Mutex<T>>
Rust Beginner 1 min read

Shared State with Arc<Mutex<T>>

When multiple threads need to share mutable data, Rust's answer is `Arc>`. `Arc` (Atomic Reference Counting) is the thread-safe version of `Rc` — it uses atomic operations to manage the reference count, making it `Send` and `Sync`. `Mutex` provides mutual exclusion: calling `.lock()` blocks until the lock is acquired and returns a `MutexGuard` smart pointer that dereferences to `&mut T`. The guard automatically releases the lock when it is dropped. The pattern is: create the `Arc>` once, clone the `Arc` for each thread (cloning an `Arc` only increments the reference count — it does not copy the data), move the clone into the thread's closure, and call `.lock().unwrap()` inside the thread to access the data. This ensures that only one thread at a time can access the inner data, preventing data races. `RwLock` is an alternative to `Mutex` that allows multiple concurrent readers or one exclusive writer. It is more efficient than `Mutex` when reads vastly outnumber writes. `Mutex::try_lock()` attempts to acquire the lock without blocking, returning `Err` if the lock is currently held. Avoiding lock contention and minimising the time spent holding a lock are key to writing performant concurrent Rust programs.
Example
use std::sync::{Arc, Mutex, RwLock};
use std::thread;

fn main() {
    // --- Arc<Mutex<T>> shared counter ---
    let counter = Arc::new(Mutex::new(0u64));
    let mut handles = vec![];

    for _ in 0..8 {
        let c = Arc::clone(&counter);
        handles.push(thread::spawn(move || {
            let mut guard = c.lock().unwrap();  // blocks until lock acquired
            *guard += 1;
        }));                                     // guard dropped → lock released
    }

    for h in handles { h.join().unwrap(); }
    println!("counter = {}", *counter.lock().unwrap());

    // --- Arc<Mutex<Vec<T>>> shared collection ---
    let log: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(Vec::new()));
    let mut handles = vec![];

    for i in 0..4 {
        let log = Arc::clone(&log);
        handles.push(thread::spawn(move || {
            let msg = format!("thread {i} reporting");
            log.lock().unwrap().push(msg);
        }));
    }
    for h in handles { h.join().unwrap(); }
    println!("log entries: {:?}", *log.lock().unwrap());

    // --- RwLock: many readers, one writer ---
    let data = Arc::new(RwLock::new(vec![1, 2, 3]));
    let r1 = Arc::clone(&data);
    let r2 = Arc::clone(&data);

    let t1 = thread::spawn(move || {
        let guard = r1.read().unwrap();
        println!("reader 1: {:?}", *guard);
    });
    let t2 = thread::spawn(move || {
        let guard = r2.read().unwrap();
        println!("reader 2: {:?}", *guard);
    });
    t1.join().unwrap(); t2.join().unwrap();

    data.write().unwrap().push(4);
    println!("after write: {:?}", *data.read().unwrap());
}