In this lesson, you will learn how to safely modify shared data across multiple threads by using Mutex to enforce mutual exclusion.
The Problem: Shared Immutability
When you share data between threads using Arc, you gain read-only access to that data. Because multiple threads could attempt to modify the same memory simultaneously, the Rust compiler prevents you from getting a mutable reference (&mut T) to that shared state. If you try to change the value, the compiler stops you—this is where Interior Mutability becomes necessary.
The Solution: Locking for Access
A Mutex<T> (Mutual Exclusion) acts as a container that enforces a "one-at-a-time" rule. To modify the data inside, a thread must first "lock" the mutex. When the lock() method is called, it blocks the thread until it can exclusively access the data.
When the lock is acquired, it returns a MutexGuard. This guard is a smart pointer that provides a mutable reference to your data. Crucially, it uses RAII (Resource Acquisition Is Initialization): when the MutexGuard goes out of scope, the lock is automatically released, allowing other threads to try their luck.
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);
let handle = thread::spawn(move || {
// Acquire the lock to get a mutable reference
let mut num = counter.lock().unwrap();
*num += 1;
// Lock is released here as `num` goes out of scope
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Result: {}", *data.lock().unwrap());
In this example, the Arc allows each thread to point to the same memory, and the Mutex provides the legal permission to change that memory. The lock() call acts as the gatekeeper, ensuring that your modifications are atomic and data-race-free. By wrapping your data in a Mutex, you transform shared, immutable state into shared, safely mutable state.
Key Takeaway: A Mutex provides interior mutability by requiring threads to acquire an exclusive lock, ensuring that only one thread can modify the inner data at any given time.