Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

`if let` does not run destructor if intermediary value [duplicate]

Consider two semantically equivalent pieces of code:

let x = Mutex::new(0);
if *x.lock().unwrap() == 0 {
    *x.lock().unwrap() = 1;
}
let x = Mutex::new(0);
if let 0 = *x.lock().unwrap() {
    *x.lock().unwrap() = 1;
}

The first one runs as expected, while the second one deadlocks. Why is guard destructor being run by if but not by if let? Where can I find documentation on this behavior?

like image 388
rubo Avatar asked Oct 15 '22 20:10

rubo


1 Answers

The answer can be found on docs.rust-lang.org. Specifically refer to if let article, where it's said that:

An if let expression is equivalent to a match expression as follows:

if let PATS = EXPR { /* body */ } else { /*else */ }

is equivalent to

match EXPR { PATS => { /* body */ }, _ => { /* else */ } }

After heading to the match article, it's said that

A match behaves differently depending on whether or not the scrutinee expression is a place expression or value expression.

If the scrutinee expression is a value expression, it is first evaluated into a temporary location, and the resulting value is sequentially compared to the patterns in the arms until a match is found. The first arm with a matching pattern is chosen as the branch target of the match, any variables bound by the pattern are assigned to local variables in the arm's block, and control enters the block.

... more about place expressions

In your case the scrutinee *x.lock().unwrap() is a value expression, thus the guard's lifetime is the same as the lifetime of your main branch. Thus you get a deadlock, trying to .lock() mutex again after it's already locked by your own if let

like image 102
Alexey Larionov Avatar answered Oct 20 '22 18:10

Alexey Larionov