How to return a nested MutexGuard?

Hello.

I need to return a locked reference to a value that is accessed through two mutexes.
Here is an example:

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

struct A {
    val: Mutex<Mutex<u32>>,
}

impl A {
    pub fn new() -> Self {
        Self {
            val: Mutex::new(Mutex::new(0)),
        }
    }

    pub fn lock(&self) -> MutexGuard<u32> {
        let first_lock = self.val.lock().unwrap();
        let second_lock = first_lock.lock().unwrap();
        second_lock
    }
}

fn main() {
    let a = A::new();

    *a.lock() = 1;
}

The code fails to compile:

error[E0515]: cannot return value referencing local variable `first_lock`
  --> src/main.rs:17:9
   |
16 |         let second_lock = first_lock.lock().unwrap();
   |                           ---------- `first_lock` is borrowed here
17 |         second_lock
   |         ^^^^^^^^^^^ returns a value referencing data owned by the current function

I expected the error, but nevertheless, I don't see how I can return second_lock while it references first_lock. Maybe there is a way to make first_lock own second_lock?

You can't. Both mutex guards must be in scope to access the inner value.

3 Likes

If you make it:

Mutex<Arc<Mutex<u32>>>

then you can clone the Arc and unlock the outer Mutex immediately. Note however that you can't return a guard borrowing from an Arc that sits in function's local variable, nor return both Arc and a reference to it together, so it will need to be a two-step process where first function call gets Arc<Mutex<u32>>, and then a second one that gets lock out of it. In practice this makes sense for types like Mutex<HashMap<K, Arc<Mutex<u32>>>> where get() is separate from later use.

2 Likes

As others have said, you can't do that.

Would it work for you to replace the lock() method with a do_locked() method that takes an FnOnce that you call with the locks held?

2 Likes

This is possible if you switch to the mutexes provided by the parking_lot crate and enable the arc_lock feature. In this case, the guard object you get back will hold its own internal clone of the Arc that was used to take the lock originally.

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.