Variable scopes extend beyond the match expression

Hey guys,
I'm pretty new to rust but I'm having decent experience in developing developing.

Here is some small code snippet, that really boggles my mind:

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

struct Test { value: i32 }
impl Test {
    fn value(&self) -> i32 { self.value }
    fn value_mut(&mut self) -> &mut i32 { &mut self.value }
}

fn do_something(value: Arc<Mutex<Test>>) {
    match { value.lock().unwrap().value() } {
        2 => {
            println!("Value is 2");
            *value.lock().unwrap().value_mut() = 3 as i32;
            println!("Value is now 3");
        },
        _ => println!("Value not 2")
    }
}

fn main() {
    let value = Arc::new(Mutex::new(Test{ value: 2 }));
    do_something(value);
}

My expectations would be, that this runs fine without any deadlock, but it dosn't.
The cause of this behaviour is, that match { value.lock().unwrap().value() } ... seems to capture the MutexGuard for the whole match body, which seems pretty odd for me, especially since { value.lock().unwrap().value() } is an independent block, returning i32.

My question is:
Is this a bug or intended behaviour? If so why (it's pretty contra intuitive for me)?

PS: This also applies for if let expressions
PPS: I tried to google a bit, but I cant really phrase my question into keywords, which lead to no satisfactory result.

This is just how Rust works - temporaries are dropped at the end of statements, not in the middle of expressions. The reference goes into detail about drop scopes on this page.

The scrutinee of a match expression is not a temporary scope, so temporaries in the scrutinee can be dropped after the match expression. For example, the temporary for 1 in match 1 { ref mut z => z }; lives until the end of the statement.

To google some more you can search for phrases like "scrutinee" and "drop order".

1 Like

Well... I think I now start understanding the thing.
My assumption was that { value.lock().unwrap().value() } is actually a block which causes the temporary MutexGuard to be drooped when evaluating it.

After some thoughts I came up with the following syntax which looks way more complicated that it should be. Is there may a better approch?

match { let x = value.lock().unwrap().value(); x } {
        2 => {
            println!("Value is 2");
            *value.lock().unwrap().value_mut() = 3 as i32;
            println!("Value is now 3");
        },
        _ => println!("Value not 2")
    }

Maybe I'm misunderstanding but why not put let x on its own line?

let x = value.lock().unwrap().value();
match x {
    // ...
}

You shouldn't be locking it twice. Instead write

fn do_something(value: Arc<Mutex<Test>>) {
    let value = value.lock().unwrap();
    match value.value() {
        2 => {
            println!("Value is 2");
            *value.value_mut() = 3 as i32;
            println!("Value is now 3");
        },
        _ => println!("Value not 2")
    }
}
3 Likes