rustmutex

Mutex unlock in function argument blocks forever


This code completes right away (playground):

use std::sync::Mutex;

fn f(_: usize, _: usize) {}

fn main() {
    let m = Mutex::new(0);

    let i = { *m.lock().unwrap() };
    let j = { *m.lock().unwrap() };
    f(i, j);
}

This code also completes right away (playground):

use std::sync::Mutex;

fn f(_: usize, _: usize) {}

fn main() {
    let m = Mutex::new(0);

    f(
        {
            let i = *m.lock().unwrap();
            i
        },
        {
            let j = *m.lock().unwrap();
            j
        },
    );
}

This code, which I think is semantically the same as the codes above, blocks forever (playground):

use std::sync::Mutex;

fn f(_: usize, _: usize) {}

fn main() {
    let m = Mutex::new(0);

    f({ *m.lock().unwrap() }, { *m.lock().unwrap() });
}

Why?


Solution

  • This happens due to temporary scopes.

    The temporary scope of an expression is the scope that is used for the temporary variable that holds the result of that expression when used in a place context, unless it is promoted.

    Apart from lifetime extension, the temporary scope of an expression is the smallest scope that contains the expression and is one of the following:

    • The entire function.
    • A statement.
    • The body of an if, while or loop expression.
    • The else block of an if expression.
    • The condition expression of an if or while expression, or a match guard.
    • The body expression for a match arm.
    • The second operand of a lazy boolean expression.

    In the third example, the smallest scope that applies to the temporary match guard is the entire statement containing the call to f. (Note that the final expression in a block is not a statement.)

    Your second and third code samples are therefore not semantically the same. In the third sample the lifetimes of the mutex guards created in the block expressions get extended to the entire statement where f is called, but in the second sample the lifetimes of the mutex guards are limited to the let statements within the corresponding block expression.