Rust Interview Questions
Rust
Web DevelopmentIoTQuestion 24
What is the Mutex
type used for in Rust?
Answer:
In Rust, the Mutex
type is used to provide mutual exclusion, which allows multiple threads to safely access and modify shared data. A Mutex
ensures that only one thread can access the protected data at a time, preventing data races and ensuring thread safety.
Key Concepts of Mutex
- Mutual Exclusion: Only one thread can lock the
Mutex
and access the data at a time. - Interior Mutability:
Mutex
allows for mutable access to data even when theMutex
itself is not mutable. - Thread Safety: Prevents data races by ensuring exclusive access to the data.
Basic Usage
Hereโs how to use Mutex
in Rust:
Example: Basic Usage of Mutex
use std::sync::Mutex;
fn main() {
let counter = Mutex::new(0);
{
let mut num = counter.lock().unwrap();
*num += 1;
} // Mutex is automatically unlocked here when `num` goes out of scope
println!("Counter: {}", *counter.lock().unwrap());
}
In this example:
- A
Mutex
is created to protect acounter
. - The
lock
method is called to acquire the lock, and it returns aMutexGuard
which allows access to the data. - The
Mutex
is automatically unlocked when theMutexGuard
goes out of scope.
Using Mutex
in Multi-Threaded Context
When working with multiple threads, you often use Mutex
in combination with Arc
(Atomic Reference Counting) to safely share the Mutex
across threads.
Example: Using Mutex
with Multiple Threads
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());
}
In this example:
Arc
is used to share theMutex
across multiple threads safely.- Each thread increments the counter protected by the
Mutex
. - The main thread waits for all threads to finish and then prints the final value of the counter.
Handling Lock Contention and Errors
- Blocking: The
lock
method will block the calling thread until it can acquire the lock. - Error Handling: The
lock
method returns aResult<MutexGuard<T>, PoisonError<MutexGuard<T>>>
. APoisonError
can occur if a thread panics while holding the lock.
Example: Handling PoisonError
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let counter1 = Arc::clone(&counter);
let handle1 = thread::spawn(move || {
let mut num = counter1.lock().unwrap();
*num += 1;
panic!("Oh no!"); // This will poison the mutex
});
let counter2 = Arc::clone(&counter);
let handle2 = thread::spawn(move || {
match counter2.lock() {
Ok(mut num) => *num += 1,
Err(poisoned) => {
let mut num = poisoned.into_inner();
*num += 1;
}
}
});
let _ = handle1.join();
let _ = handle2.join();
println!("Result: {}", *counter.lock().unwrap());
}
In this example:
- If the first thread panics, it will poison the
Mutex
. - The second thread handles the
PoisonError
by callinginto_inner
to access the data.
Summary
- Purpose:
Mutex
is used for ensuring mutual exclusion, allowing safe mutable access to shared data across threads. - Basic Usage: Lock the
Mutex
to access the data, and it unlocks automatically when the lock guard goes out of scope. - Multi-Threaded Context: Use
Arc
withMutex
to safely share and mutate data across multiple threads. - Error Handling: Handle
PoisonError
if a thread panics while holding the lock.
Mutex
is a fundamental tool for synchronizing access to shared data in Rust, ensuring thread safety and preventing data races.