Rust Lesson 40 – Mutex | Dataplexa

Mutexes in Rust

In this lesson, you will learn how to use mutexes in Rust to safely share data between multiple threads. Mutexes allow controlled access to shared data while preventing data races.

Rust combines mutexes with its ownership system to provide safe and reliable shared-state concurrency.


What Is a Mutex?

A mutex (short for mutual exclusion) is a synchronization primitive that allows only one thread to access a piece of data at a time.

When one thread locks the mutex, other threads must wait until the lock is released.


Why Use Mutexes?

Mutexes are used when:

  • Multiple threads need access to the same data
  • Data must not be modified concurrently
  • Channels are not sufficient

Rust ensures mutex usage is safe and predictable.


Mutex in Rust

Rust provides mutexes through the std::sync::Mutex type.

A mutex wraps the data it protects.

use std::sync::Mutex;

fn main() {
    let counter = Mutex::new(0);
}

Locking a Mutex

To access the data inside a mutex, you must acquire a lock.

use std::sync::Mutex;

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

    let mut num = counter.lock().unwrap();
    *num += 1;
}

The lock is released automatically when the guard goes out of scope.


Sharing a Mutex Across Threads

To share a mutex between threads, Rust uses Arc (Atomic Reference Counting).

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

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

    for _ in 0..5 {
        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());
}

Mutex Guard

The value returned by lock() is called a mutex guard.

It implements the Deref trait, allowing you to access the data easily.


Handling Lock Errors

If a thread panics while holding a mutex, the mutex becomes poisoned.

Rust allows you to handle this safely.

match counter.lock() {
    Ok(guard) => println!("{}", *guard),
    Err(poisoned) => println!("{}", *poisoned.into_inner()),
}

Deadlocks

A deadlock occurs when two or more threads wait indefinitely for each other.

To avoid deadlocks:

  • Lock mutexes in a consistent order
  • Keep lock scope small
  • Avoid nested locks

When to Use Mutex vs Channel

Choose mutexes when:

  • Shared mutable state is required
  • Data must be updated by multiple threads

Choose channels when:

  • Threads communicate via messages
  • Shared state can be avoided

Performance Considerations

Mutexes introduce locking overhead.

Use them carefully and only when necessary.


📝 Practice Exercises


Exercise 1

Create a mutex protecting an integer.

Exercise 2

Lock a mutex and modify its value.

Exercise 3

Share a mutex across multiple threads using Arc.

Exercise 4

Explain how Rust prevents data races with mutexes.


✅ Practice Answers


Answer 1

let m = Mutex::new(5);

Answer 2

*m.lock().unwrap() += 1;

Answer 3

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

Answer 4

Rust enforces exclusive access through locking and ownership rules.


What’s Next?

In the next lesson, you will learn about Atomic Types in Rust, which provide lock-free concurrency for simple shared data.