Rust Guide > Documentation > Concurrency > Mutex

Introduction

The Mutex class in Rust provides mutual exclusion, allowing only one thread to access the data at a time. It is a fundamental synchronization primitive useful for protecting shared data from being simultaneously accessed by multiple threads.

Using Mutex

The Mutex class can be used to create a thread-safe wrapper around a piece of data. Here is a basic example:

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

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

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

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

    println!("Result: {}", *counter.lock().unwrap());
}
// Output: Result: 10

Key Methods

Below are some of the key methods exposed by the Mutex class:

new

Creates a new Mutex instance wrapping the given data.

use std::sync::Mutex;

fn main() {
    let m = Mutex::new(5);
    {
        let mut num = m.lock().unwrap();
        *num = 6;
    }
    println!("m = {:?}", m);
}
// Output: m = Mutex { data: 6 }

lock

Acquires a lock on the mutex, blocking the current thread until it is able to do so. Returns a MutexGuard which allows access to the underlying data.

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

fn main() {
    let data = Arc::new(Mutex::new(0));

    let data_clone = Arc::clone(&data);
    let handle = thread::spawn(move || {
        let mut num = data_clone.lock().unwrap();
        *num = 42;
    });

    handle.join().unwrap();
    println!("Data: {}", *data.lock().unwrap());
}
// Output: Data: 42

try_lock

Attempts to acquire the lock on the mutex without blocking. Returns a Result which is Ok(MutexGuard) if the lock was acquired, or Err(TryLockError) if it was not.

use std::sync::{Mutex, TryLockError};

fn main() {
    let m = Mutex::new(5);

    match m.try_lock() {
        Ok(mut num) => *num = 6,
        Err(TryLockError::WouldBlock) => println!("Could not acquire lock"),
        Err(TryLockError::Poisoned(err)) => println!("Lock is poisoned: {}", err),
    }

    println!("m = {:?}", m);
}
// Output: m = Mutex { data: 6 }

into_inner

Consumes the mutex, returning the underlying data. This will block if the mutex is currently locked.

use std::sync::Mutex;

fn main() {
    let m = Mutex::new(5);
    let data = m.into_inner().unwrap();
    println!("Data: {}", data);
}
// Output: Data: 5

get_mut

Returns a mutable reference to the underlying data without acquiring the lock. This method is safe to call only when there are no active references to the mutex.

use std::sync::Mutex;

fn main() {
    let mut m = Mutex::new(5);
    *m.get_mut().unwrap() = 6;
    println!("m = {:?}", m);
}
// Output: m = Mutex { data: 6 }

Example Usage

Example 1: Basic Usage

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

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

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

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

    println!("Result: {}", *counter.lock().unwrap());
}
// Output: Result: 10

Example 2: Lock and Modify Data

use std::sync::Mutex;

fn main() {
    let m = Mutex::new(5);
    {
        let mut num = m.lock().unwrap();
        *num = 6;
    }
    println!("m = {:?}", m);
}
// Output: m = Mutex { data: 6 }

Example 3: Try Lock

use std::sync::{Mutex, TryLockError};

fn main() {
    let m = Mutex::new(5);

    match m.try_lock() {
        Ok(mut num) => *num = 6,
        Err(TryLockError::WouldBlock) => println!("Could not acquire lock"),
        Err(TryLockError::Poisoned(err)) => println!("Lock is poisoned: {}", err),
    }

    println!("m = {:?}", m);
}
// Output: m = Mutex { data: 6 }

Example 4: Into Inner

use std::sync::Mutex;

fn main() {
    let m = Mutex::new(5);
    let data = m.into_inner().unwrap();
    println!("Data: {}", data);
}
// Output: Data: 5

Example 5: Get Mut

use std::sync::Mutex;

fn main() {
    let mut m = Mutex::new(5);
    *m.get_mut().unwrap() = 6;
    println!("m = {:?}", m);
}
// Output: m = Mutex { data: 6 }

Considerations

  • Mutex can cause deadlocks if not used carefully. Always ensure that locks are released appropriately.
  • A Mutex can become poisoned if a thread panics while holding the lock. Accessing a poisoned Mutex will result in a PoisonError.
  • Use Arc (Atomic Reference Counting) to share a Mutex across multiple threads.

See Also

  • RwLock - A reader-writer lock for allowing concurrent reads or exclusive writes.
  • Condvar - A condition variable for thread synchronization.
  • AtomicUsize - A thread-safe atomic integer type.
  • Arc - An atomic reference-counted smart pointer for shared ownership across threads.