1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-09-16 09:36:41 +02:00

address @randomPoison feedback 2/2

split mutex (RAII) chapter in two parts
This commit is contained in:
Glen De Cauwsemaecker
2025-08-30 09:16:59 +02:00
parent cc5f3b5af6
commit 55e4753e08
3 changed files with 91 additions and 37 deletions

View File

@@ -439,6 +439,7 @@
- [Is It Encapsulated?](idiomatic/leveraging-the-type-system/newtype-pattern/is-it-encapsulated.md)
- [RAII](idiomatic/leveraging-the-type-system/raii.md)
- [Mutex](idiomatic/leveraging-the-type-system/raii/mutex.md)
- [Drop Guards](idiomatic/leveraging-the-type-system/raii/drop_guards.md)
- [Drop Bomb](idiomatic/leveraging-the-type-system/raii/drop_bomb.md)
- [Scope Guard](idiomatic/leveraging-the-type-system/raii/scope_guard.md)

View File

@@ -0,0 +1,85 @@
# Drop Guards
A **drop guard** in Rust is a temporary _RAII_ guard that executes a specific
action when it goes out of scope.
It acts as a wrapper around a value, ensuring that some cleanup or secondary
behavior happens automatically when the guard is dropped.
One of the most common examples is `MutexGuard`, which represents temporary
exclusive access to a shared resource.
```rust
#[derive(Debug)]
struct Mutex<T> {
value: std::cell::UnsafeCell<T>,
is_locked: std::sync::atomic::AtomicBool,
}
#[derive(Debug)]
struct MutexGuard<'a, T> {
value: &'a mut T,
mutex: &'a Mutex<T>,
}
impl<T> Mutex<T> {
fn new(value: T) -> Self {
Self {
value: std::cell::UnsafeCell::new(value),
is_locked: std::sync::atomic::AtomicBool::new(false),
}
}
fn lock(&self) -> MutexGuard<'_, T> {
// Acquire the lock and create the guard object.
if self.is_locked.swap(true, std::sync::atomic::Ordering::AcqRel) {
todo!("Block until the lock is released");
}
let value = unsafe { &mut *self.value.get() };
MutexGuard { value, mutex: self }
}
}
impl<'a, T> Drop for MutexGuard<'a, T> {
fn drop(&mut self) {
self.mutex.is_locked.store(false, std::sync::atomic::Ordering::Release);
}
}
fn main() {
let m = Mutex::new(vec![1, 2, 3]);
let mut guard = m.lock();
guard.value.push(4);
guard.value.push(5);
println!("{guard:?}");
}
```
<details>
- The example above shows a simplified `Mutex` and its associated guard. Even
though it is not a production-ready implementation, it illustrates the core
idea: the guard enforces exclusive access, and its `Drop` implementation
guarantees that the lock is released when the guard goes out of scope.
- A few things are left out for brevity:
- `Deref` and `DerefMut` implementations for `MutexGuard`, which would allow
you to use the guard as if it were a direct reference to the inner value.
- Making `.lock()` truly blocking, so that it waits until the mutex is free
before returning.
- In addition, a `.try_lock()` method could be added to provide a
non-blocking alternative, returning `Option::None` or `Result::Err(...)`
if the mutex is still locked.
- Panics are not explicitly handled in the `Drop` implementation here. In
practice, one can use `std::thread::panicking()` to check if the guard was
dropped during a panic.
- The standard library’s `std::sync::Mutex` uses this to implement
**poisoning**, where a mutex is marked as poisoned if a panic occurs while
holding the lock, since the protected value may now be in an inconsistent
state.
</details>

View File

@@ -1,4 +1,4 @@
# Mutex
# Mutex and MutexGuard
In earlier examples, RAII was used to manage concrete resources like file
descriptors. With a `Mutex`, the resource is more abstract: exclusive access to
@@ -8,46 +8,14 @@ Rust models this using a `MutexGuard`, which ties access to a critical section
to the lifetime of a value on the stack.
```rust
#[derive(Debug)]
struct Mutex<T> {
value: std::cell::UnsafeCell<T>,
// [...]
}
#[derive(Debug)]
struct MutexGuard<'a, T> {
value: &'a mut T,
// [...]
}
impl<T> Mutex<T> {
fn new(value: T) -> Self {
Self {
value: std::cell::UnsafeCell::new(value),
// [...]
}
}
fn lock(&self) -> MutexGuard<T> {
// [...]
let value = unsafe { &mut *self.value.get() };
MutexGuard { value }
}
}
impl<'a, T> Drop for MutexGuard<'a, T> {
fn drop(&mut self) {
// [...]
println!("drop MutexGuard");
}
}
use std::sync::Mutex;
fn main() {
let m = Mutex::new(vec![1, 2, 3]);
let mut guard = m.lock();
guard.value.push(4);
guard.value.push(5);
let mut guard = m.lock().unwrap();
guard.push(4);
guard.push(5);
println!("{guard:?}");
}
```