Rust Notes on Temporary values (usage of Mutex) - 2
Tags: rust, mutex
Published: 2023-06-16 04:26:57

---
title: Rust Notes on Temporary values (usage of Mutex) - 2
published: true
description: A collection of notes I have made, while learning Rust
tags: #rust #mutex
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fieo0rdpxxt5daynrb4i.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-06-16 04:20 +0000
---

[Cover image credit: https://pixabay.com/users/engin_akyurt-3656355]


### Build up

In [this](https://dev.to/nsengupta/rust-notes-on-temporary-values-in-method-call-expressions-1-1g8) article (first in the series), I have elaborated the understanding of temporary objects / variables in an expression, their scopes and behaviour.

This article is a continuation of the same topic, but it explores a much-used (and very crucial in multi-threaded context) type in Rust programming:  the `Mutex` .

### Quick definition for the context

The concept of a Mutex (**Mut**ual **Ex**clusion) is ages old. The idea is to ensure that only one thread of execution can access and modify a piece of data in memory, it must be locked first and unlocked, post-modification. All threads must govern themselves by checking for the lock's availabilty before going ahead with the modification; if the lock is not avaiable (meaning, some other thread is in the middle of modifying the data beind the lock), it must not gatecrash but wait, patiently.The Rust bool's chapter on [Mutex](https://doc.rust-lang.org/book/ch16-03-shared-state.html?highlight=mutex#using-mutexes-to-allow-access-to-data-from-one-thread-at-a-time) is a recommended read.

### Mutex in Rust

The crucial and tricky thing about a Mutex is about when to put in the lock around the inner data (rememember: a lock's existence is jutsified because it is _guarding_ some piece of data in memory) and when to lift it. The sequecnce of _acquiring_ and _releasing_ the lock extremely important. Before we try to understand the way it is done in Rust, we should get familiarized with a term: RAII.

#### RAII

RAII stands for, rather too predictably, **R**esource **A**cquisition **I**s **I**nitialization (phew!). C++ experts are quite familiar with it. I will not delve deep into this topic - other detailed descriptions like [this](https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization), [this](https://learn.microsoft.com/en-us/cpp/cpp/object-lifetime-and-resource-management-modern-cpp?view=msvc-170) and [this](https://www.youtube.com/watch?v=uT3wL9K0DoQ) and several others - exist for a deeper understanding. 

Put very simply, RAII is a mechanism which stipulates that any access to a computing resource (viz., memory,file, DB Connection etc.) must be preceded by its construction. While this sounds kind of given, the subsequent part is what is important: the access to resource is denied after the destruction of the resource. In other words, the resource is available strictly between its creation and demise.The duration between these two stages of life, is determined by the scoping rules.

#### OBRM

I have come across this term while tinkering with Rust. OBRM stands for *O*wnership *B*ased *R*esource *M*anagament (you don't like the acronym? I don't, either :smiley: ). Again, I will not delve into this either (I find these 2 good videos by #letsgorusty [here](https://www.youtube.com/watch?v=AnFaf-L_DfE) and [here](https://www.youtube.com/watch?v=7EcNkr6KFy0), quite explanatory). The main understanding is this: acquisition and release of resources are governed by the *Ownership* rules of Rust. Put simply, when the object (say, a constructed `struct`) that holds the resource is destroyed ( *Drop*ped ), the resource is released too. Automatically! The language ensures this behavior!

My objective is to elucidate how this idiom is used in Rust's handling of `Mutex`.

### Experiments and observations

We hold a simple `i32` value inside a `Mutex`, then operate on it, observe the behavior of the code and convince ourselves why the behavior is that way!

```rust
use std::sync::Mutex;
fn main() {
let a_mutex = Mutex::new(5);         // Just an i32 value inside a mutex
    let guard = a_mutex.lock().unwrap(); // <-- 'guard' is of type `MutexGuard`
    println!("guard -> {:?}", guard);
    println!("Mutex itself -> {:?}", a_mutex);
    println!("Good bye!");
}
```
The output is:

```bash
guard -> 5
Mutex itself -> Mutex { data: <locked>, poisoned: false, .. }
Good bye!
```
Inside itself, the `Mutex` is holding its own `data` in a *Locked* state. Then the program ends, and the lock is released. But, where is that `MutexGuard` coming from? 

Here's what the rust-lang documentation says, of the `lock` method:

![The portion of Mutex.lock() from Rust API doc](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w87n2ddcna6j7kzit6y7.png)

It returns a `LockResult`, which when transformed using an `unwrap()` like in the code above, produces a `MutexGuard` which is guarding an `i32` (the type T) in this case.

What it we try to acquuire the lock again, on the same `a_mutex`?

```rust
use std::sync::Mutex;
fn main() {
    let a_mutex = Mutex::new(5);
    let guard = a_mutex.lock().unwrap(); // <-- 'guard' is of type `MutexGuard`
    println!("guard -> {:?}", guard);
    println!("Mutex itself -> {:?}", a_mutex);

    // An attempt to acquire the lock again.
    let w = a_mutex.lock().unwrap();
    println!("Good bye!");
}
```

This time, the output is:

```rust
guard -> 5
Mutex itself -> Mutex { data: <locked>, poisoned: false, .. }
```

The program **never** terminates (it cannot even say 'Good Bye')! Why? This is what the documentation says:

> The exact behavior on locking a mutex in the thread which already holds the lock is left unspecified. However, this function will not return on the second call (it might panic or deadlock, for example).

In our case, we made a second call, on a mutex, in the same thread (the *main* thread here, there is only one)!

`Mutex` has a helpful alternative, in the form of `Mutex::try_lock()`, which allows us to attempt to acquire the lock, **knowing** that we may **fail**.

```rust
use std::sync::Mutex;

fn main() {
    let a_mutex = Mutex::new(5);
    let guard = a_mutex.lock().unwrap(); // <-- 'guard' is of type `MutexGuard`
    println!("guard -> {:?}", guard);
    println!("Mutex itself -> {:?}", a_mutex);

    // An attempt to acquire the lock again.
    let w = a_mutex.try_lock().unwrap();
    println!("Good bye!");
}
```

This time, the output is:

```bash
guard -> 5
Mutex itself -> Mutex { data: <locked>, poisoned: false, .. }
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: "WouldBlock"', src/main.rs:37:32
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```

The error message is quite explanatory. The attempt to acquire the lock again, fails because the it will have caused a forever blocking and therefore, non-termination of the program as we have seen eariler.

The point of interest is this part from rust-lang documentation:

> This function will block the local thread until it is available to acquire the mutex. Upon returning, the thread is the only thread with the lock held. An **RAII guard is returned** to allow scoped unlock of the lock. When the guard **goes out of scope**, the mutex will be **unlocked**.

 The type of this RAII guard is `MutexGuard`, (my comment on the code-snippet). What is its purpose in life?

 Again, rust-lang documentation tells us in comforting details:

 >An RAII implementation of a “scoped lock” of a mutex. When this structure is dropped (falls out of scope), the lock will be unlocked.
 >The data protected by the mutex can be accessed through this guard via its Deref and DerefMut implementations.
 >This structure is **created by the lock** and try_lock methods on Mutex.

The key portion, in our context, is this: "When this structure is dropped (falls out of scope), the lock will be unlocked".

### In search of the 'Scope'

Let us try and find out what is the scope of the guard.

```rust
fn main() {
    use std::sync::Mutex;

    let a_mutex = Mutex::new(5);

    {   // <-- Beginning of scope
        let guard = a_mutex.lock().unwrap(); // <-- 'guard' is of type `MutexGuard`
        println!("guard -> {:?}", guard);
    }   // <-- End of scope`
    
    println!("Mutex itself -> {:?}", a_mutex);
    println!("Good bye!");
}
```

The output is quite as expected.

```bash
guard -> 5
Mutex itself -> Mutex { data: 5, poisoned: false, .. }
Good bye!
```

Don't miss the fact that `Mutex { data: 5, poisoned: false, .. }` indicates the absence of the lock being held, as opposed to the output that we have seen earlier: `Mutex { data: <locked>, poisoned: false, .. }`. The following achieves the same effect, for the same reason:

```rust
fn main() {
    use std::sync::Mutex;

    let a_mutex = Mutex::new(5);
    let guard = a_mutex.lock().unwrap(); // <-- 'guard' is of type `MutexGuard`
    println!("guard -> {:?}", guard);
    drop(guard);  // <-- Dropping manually!

    println!("Mutex itself -> {:?}", a_mutex);
    println!("Good bye!");
}
```

The output is exactly the same as the precding output ( *no lock* ).

We establish then, that when the guard is dropped, the lock is released **automatically**, as per the premise of RAII (or OBRM, if you like). But, when is it constructed exactly?

Let's be a bit adventurous and peep into the implementation of `std::sync::Mutex.rs`:

```rust
#[stable(feature = "rust1", since = "1.0.0")]
    pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> {
        unsafe {
            self.inner.lock();
            MutexGuard::new(self)
        }
    }
```

Let's ignore everything except, the return type and the call to `MutexGuard::new(self)`. Even without much knowledge about the internals, it is quite clear that when `Mutex::lock()` returns, it carries with itself, freshly built `MutexGuard` object. Just before that happens, a lock resource (Operating System resource) has been obtained. The initialization has guaranteed the acquisition! 

How about release of the lock? Again, peeping into the source of `std::sync::Mutex.rs`:

```rust
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Drop for MutexGuard<'_, T> {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            self.lock.poison.done(&self.poison);
            self.lock.inner.unlock();
        }
    }
}
```

The key point for us is that `self.lock.inner.unlock()` statement. As the `MutexGuard` is dropped, the lock is released!

Therefore, the **scope** in which a `MutexGuard` remains valid, follows the idiom of **RAII**!

### The temporary MutexGuard

The preceding [article](https://dev.to/nsengupta/rust-notes-on-temporary-values-in-method-call-expressions-1-1g8) in this series discusses the concept of temporary variable. One of the takeaways from that is the presence of a *receiver*, which is *temporarily* produced to complete the method-call expression.

The concept of this temporary object is applicable in the case of `MutexGuard` here too. And, following the scoping rules of a temporary, the `MutexGuard` ceases to exist at the end of the statement.

```rust
fn main() {

    use std::sync::Mutex;

    let a_mutex = Mutex::new(5);
    let guard = a_mutex.lock().unwrap().to_string(); // <-- 'guard' is of type `String`

    println!("Mutex itself -> {:?}", a_mutex); // <-- MutexGuard is not being held
    println!("guard -> {:?}", guard);
    
    // An attempt to acquire the lock again.
    let w = a_mutex.try_lock().unwrap();
    println!("Good bye!");
}
```
Nothing surprising about the output:

```bash
guard -> "5"
Mutex itself -> Mutex { data: 5, poisoned: false, .. }
Good bye!
```

Notice the absence of any `data: <locked>` in the Mutex. When the preceding statement ends - yielding the stringified representation of value '5' - the tempoary MutexGuard
*   reaches the end of its *scope*,
*   is dropped, and therefore
*   releases the lock (re: RAII)

------------------------------------------------------------------------------------------------


We have read the value that is fenced in by the Mutex and have trodden in the world of **expressions** along with temporary objects. In the next article in this series, we explore more about how the **scope** of the temporaries is affected by the body of an expression.

### Main Takeaways

*   `Mutex` follows the idiom of RAII (or OBRM) for ensuring that access to the value it is guarding, cannot be breached.
*   `Mutex.lock()` produces a temporary `MutexGuard` whose construction and destruction ( Drop ), acquires and releases the lock, in that order.
*   Because it is a temporary object, the `MutexGuard` is dropped at the end of the statement that creates it, unless it scope is expanded by other means (that is not a part of this article though).