In this lesson, you will learn how to use a Mutex to safely allow multiple parts of your program to modify the same data without causing memory corruption.
The Problem: Shared Mutable State
In Rust, the compiler strictly enforces that you can only have one mutable reference to a piece of data at a time. This prevents "data races," where two parts of your program try to change the same memory simultaneously, leading to unpredictable crashes. However, when building complex programs that perform multiple tasks at once, you often need to share data. How do you satisfy the compiler while keeping your data safe?
Enter the Mutex
A Mutex (short for Mutual Exclusion) acts like a single key to a room. If you want to change the data inside, you must "lock" the Mutex. If another part of your code already has the key, your code will pause and wait patiently until the key is returned.
Think of it as a gatekeeper that ensures only one thread of execution can access the protected data at any given moment.
How to use it
To use a Mutex, you wrap your data inside it. When you call .lock(), you get back a smart pointer that provides access to the data. When that smart pointer goes out of scope, the lock is automatically released.
use std::sync::Mutex;
fn main() {
// Wrap our integer in a Mutex
let counter = Mutex::new(0);
// Lock the mutex to get access to the data
{
let mut num = counter.lock().unwrap();
*num += 1;
} // The lock is automatically dropped here
println!("Count is: {:?}", counter.lock().unwrap());
}
In this example, the lock() method returns a Result (because the lock might be "poisoned" if another thread panicked while holding it), so we use .unwrap() to access the inner value. By locking, we guarantee that no one else can touch that 0 while we are busy incrementing it to 1. Once the block ends, the lock is released, and others may safely claim it.
Key Takeaway: A Mutex provides safe interior mutability by ensuring only one owner can access the shared data at a time, preventing data races by design.