Skip to content

A pitfall that a temporary will not be dropped at the end of block expression it has been created in #83402

Closed
@viruscamp

Description

@viruscamp

I have checked #21114 #37612 #60107 and #78390 .

I tried this code:

use std::sync::Mutex;
fn main() {
    dead_lock_2();
}

fn dead_lock_1() {
    let vec_mutex = Mutex::new(vec![1,2,3]);
    // It's known that the temporary MutexGuard will be dropped at the end of while let, and will cause a dead lock.
    while let Some(num) = vec_mutex.lock().unwrap().pop() {
        if num == 2 {
            vec_mutex.lock().unwrap().push(4);
        }
        println!("got {}", num);
    }
}

fn dead_lock_2() {
    let vec_mutex = Mutex::new(vec![1,2,3]);
    // I add a scope, and expect the temporary MutexGuard dropped at the end of the scope. It does not work as I expect.
    while let Some(num) = {
        vec_mutex.lock().unwrap().pop()
    } {
        if num == 2 {
            vec_mutex.lock().unwrap().push(4);
        }
        println!("got {}", num);
    }
}

fn no_dead_lock() {
    let vec_mutex = Mutex::new(vec![1,2,3]);
    // It works. 
    while let Some(num) = {
        let num = vec_mutex.lock().unwrap().pop();
        num
    } {
        if num == 2 {
            vec_mutex.lock().unwrap().push(4);
        }
        println!("got {}", num);
    }
}

I expected to see this happen: the dead_lock_2 function got outputs as expect.

got 3
got 2
got 4
got 1

Instead, this happened: the dead_lock_2 function got deadlock unexpected.

got 3
<--dead lock occurs-->

Meta

rustc --version --verbose:

rustc 1.52.0-nightly (36f1f04f1 2021-03-17)
binary: rustc
commit-hash: 36f1f04f18b89ba4a999bcfd6584663fd6fc1c5d
commit-date: 2021-03-17
host: x86_64-pc-windows-msvc
release: 1.52.0-nightly
LLVM version: 12.0.0

I have got the answer: #37612 (comment)
The drop scope of the temporary is usually the end of the enclosing statement.
But a block expression is not a statement, so { vec_mutex.lock().unwrap().pop() } will not drop the temporary.

It's a pitfall, so I think it should be:

  1. Add it clearly in document: https://doc.rust-lang.org/stable/reference/expressions.html#temporaries : Temporaries created in a block expression will not be dropped at the end of the block.
  2. Or change the behavior: The end of a block expression is also the drop scope of the temporary created in the block.

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions