Search code examples
rustcondition-variable

How do I use a Condvar without moving the Mutex variable?


I want to use a Condvar in my program. Below is a short test of it. I know it will be stuck forever on the loop.

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

fn main() {
    let var_in_lock = Arc::new(Mutex::new(8));
    let cvar = Arc::new(Condvar::new());

    let unlocked = var_in_lock.lock().unwrap();

    loop {
        cvar.wait(unlocked).unwrap();
    }
}

It doesn't compile:

error[E0382]: use of moved value: `unlocked`
  --> src/main.rs:10:19
   |
7  |     let unlocked = var_in_lock.lock().unwrap();
   |         -------- move occurs because `unlocked` has type `std::sync::MutexGuard<'_, i32>`, which does not implement the `Copy` trait
...
10 |         cvar.wait(unlocked).unwrap();
   |                   ^^^^^^^^ value moved here, in previous iteration of loop

I have looked at the example in the Rust documentation. The two differences I spot are:

  1. They create the Condvar and Mutex in a pair. This shouldn't matter, right? I want to create them separately.
  2. They match the lock using the ref keyword, which, if I understand this answer correctly, means that a reference to the lock is made. I figured that by changing the line to cvar.wait(&unlocked).unwrap(); would be the same thing, but then the compiler complains that a MutexGuard, and not a reference, is expected.

How can I make it compile?


Solution

  • The difference that you didn't spot is that wait returns the MutexGuard:

    pub fn wait<'a, T>(
        &self,
        guard: MutexGuard<'a, T>
    ) -> LockResult<MutexGuard<'a, T>>
    
    while !*started {
        started = cvar.wait(started).unwrap();
    }
    

    wait takes ownership of the MutexGuard since it releases the lock and later reacquires it. Transferring ownership statically prevents using an improperly locked (or unlocked) variable, an example of using Rust's type system to the programmers advantage.


    You need to do the same thing in your code:

    let mut unlocked = var_in_lock.lock().unwrap();
    
    loop {
        unlocked = cvar.wait(unlocked).unwrap();
    }
    

    See also: