Mutex<T>
std::sync::Mutex<T> (Mutual Exclusion) is a thread-safe interior mutability container that protects shared data by allowing at most one thread to access the inner value T at any given time. Unlike C or Java where a mutex is a standalone lock object separate from the data it protects, a Rust Mutex<T> owns T directly inside the lock, making it impossible to access T without first acquiring the lock.
Acquiring a Lock and MutexGuard<'_, T>
Calling .lock() blocks the current thread until the mutex is available and returns a LockResult<MutexGuard<'_, T>>. The returned MutexGuard<'_, T> is an RAII smart pointer that:
- Implements
Deref<Target = T>andDerefMut<Target = T>so you can read and mutate the protected data through*guardor method calls. - Automatically unlocks the mutex in its
Dropimplementation when the guard leaves scope (even during a panic unwind).
Mutex<T> Access Flow:
Thread A calls mutex.lock() ---> Acquires OS/futex lock
|
v
Returns MutexGuard<'_, T>
(Deref & DerefMut to T)
|
v
Guard goes out of scope (Drop) ---> Automatically unlocks
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let balance = Arc::new(Mutex::new(100_i64));
thread::scope(|s| {
for delta in [50_i64, -30, 80] {
let balance = Arc::clone(&balance);
s.spawn(move || {
let mut guard = balance.lock().unwrap();
*guard += delta;
}); // `guard` drops here, releasing the lock immediately.
}
});
println!("final balance: {}", *balance.lock().unwrap());
}
final balance: 200
Core Mutex<T> Methods
| Method | Signature | Behavior |
|---|---|---|
Mutex::new(val) |
const fn new(t: T) -> Mutex<T> |
Creates an unlocked mutex holding t (const-constructible, usable in static) |
.lock() |
fn lock(&self) -> LockResult<MutexGuard<'_, T>> |
Blocks current thread until lock is acquired |
.try_lock() |
fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T>> |
Attempts to acquire lock immediately without blocking; returns Err(TryLockError::WouldBlock) if held |
.into_inner() |
fn into_inner(self) -> LockResult<T> |
Consumes the Mutex and moves out T without locking |
.get_mut() |
fn get_mut(&mut self) -> LockResult<&mut T> |
Returns &mut T with zero locking overhead when caller holds exclusive &mut Mutex<T> |
.clear_poison() |
fn clear_poison(&self) |
Clears the poisoned flag after inspecting/repairing the inner state |
Lock Poisoning
If a thread panics while holding a MutexGuard<'_, T>, the guard's destructor still unlocks the mutex so other threads do not deadlock, but it marks the Mutex<T> as poisoned because the panicking thread may have left T in a partially updated state. Subsequent calls to .lock() return Err(PoisonError<MutexGuard<'_, T>>).
Callers can either:
- Propagate the panic with
.lock().unwrap(). - Recover the guard with
.lock().unwrap_or_else(|poisoned| poisoned.into_inner())after restoring invariants.
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let shared = Arc::new(Mutex::new(vec![1, 2]));
let shared_clone = Arc::clone(&shared);
let _ = thread::spawn(move || {
let mut guard = shared_clone.lock().unwrap();
guard.push(3);
panic!("simulated failure mid-update");
})
.join();
println!("is_poisoned: {}", shared.is_poisoned());
// Recover the underlying guard despite poisoning:
let guard = shared
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
println!("recovered data: {:?}", *guard);
}
is_poisoned: true
recovered data: [1, 2, 3]
Avoiding Deadlocks by Dropping Guards Early
A Mutex<T> is not re-entrant: calling .lock() a second time on the same thread while an earlier MutexGuard is still alive deadlocks the thread. Calling drop(guard) explicitly (or using an inner block { ... }) before performing I/O or acquiring another lock minimizes lock contention and prevents self-deadlock.