Course contentsShow
← Rust
Lesson 1028 of 1,9109. Smart Pointers and Interior MutabilityFree lesson

Interior Mutability in Multi-threaded Contexts

Combine Mutex with Arc to achieve thread-safe shared mutability.

In this lesson, you will learn how to combine Arc and Mutex to safely mutate shared state across multiple threads.

The Problem: Immutable Sharing

As you have learned, Arc allows multiple threads to own a piece of data by providing shared, immutable access. However, because Arc only provides &T (a shared reference), you cannot modify the contents of the data it wraps. If you try to update a value, the compiler will stop you, as it cannot guarantee that other threads aren't reading the data at the same moment.

The Solution: Wrapping for Mutability

To enable safe mutation, we must introduce interior mutability. While Cell and RefCell provide interior mutability for single-threaded contexts, they are not Sync and will trigger compiler errors if you try to share them across threads.

Instead, we use a Mutex (Mutual Exclusion). A Mutex provides a synchronization primitive that grants exclusive access to its inner data. By wrapping your data in a Mutex, you gain a lock() method. When you call lock(), the thread blocks until it gains exclusive access, at which point it receives a MutexGuard. This guard acts as a smart pointer that allows you to modify the data, and it automatically releases the lock when it goes out of scope.

Putting it Together

To share this mutability across threads, we nest these two types: Arc<Mutex<T>>. The Arc handles the shared ownership across threads, and the Mutex handles the interior mutability once a thread has acquired access.

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

let data = Arc::new(Mutex::new(0));

let mut handles = vec![];

for _ in 0..10 {
    let counter = Arc::clone(&data);
    handles.push(thread::spawn(move || {
        let mut num = counter.lock().unwrap(); // Acquire access
        *num += 1;                             // Mutate safely
    }));
}

for handle in handles {
    handle.join().unwrap();
}

println!("Final count: {}", *data.lock().unwrap());

In this pattern, the Arc keeps the memory alive, and the Mutex ensures that only one thread can modify the count at any given time, preventing data races.

Key Takeaway: Use Arc<Mutex<T>> to achieve thread-safe shared mutability by combining the shared ownership of Arc with the exclusive runtime access provided by Mutex.