Skip to content

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

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
viruscamp opened this issue Mar 23, 2021 · 1 comment
Labels
C-bug Category: This is a bug.

Comments

@viruscamp
Copy link

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.
@viruscamp viruscamp added the C-bug Category: This is a bug. label Mar 23, 2021
@csmoe
Copy link
Member

csmoe commented Mar 23, 2021

The doc clarify about this behavior is in the destructors chapter.
Feel free to reopen for more questions :)

@csmoe csmoe closed this as completed Mar 23, 2021
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
C-bug Category: This is a bug.
Projects
None yet
Development

No branches or pull requests

2 participants