Description
This came up recently on twitter: https://twitter.com/fasterthanlime/status/1491803585385877519
Where @fasterthanlime ran into an unexpected difference in behavior that caused a deadlock.
The following code works fine:
use parking_lot::Mutex;
struct State {}
impl State {
fn foo(&self) -> bool {
true
}
fn bar(&self) {}
}
fn main() {
let mutex = Mutex::new(State {});
let val = mutex.lock().foo();
match val {
true => {
mutex.lock().bar();
}
false => {}
};
println!("All done!");
}
But when the mutex locking expression is inlined into the match scrutinee expression it causes deadlock:
match mutex.lock().foo() {
true => {
mutex.lock().bar();
}
false => {}
};
Link to playgrounds
- Working: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=cb6f3a378805e4bbff601cee783af7f4
- Deadlock: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=09d0aed9879678812aea960bd87dc263
This deadlock occurs because temporaries created in a match scrutinee automatically have their lifetime extended for the duration of the match expression. This is done to allow match arms to borrow from the scrutinee expressions, but this surprised many of the users in the above twitter thread because foo
returns a bool, so there is no borrow taken by any of the match arms. My understanding is that the current behavior was chosen because otherwise drop timing of match temporaries would depend on whether or not you create any borrows.
This issue has generated some discussion around the possibility of changing drop rules in an edition to more aggressively drop temporaries. In this issue I would like to avoid a discussion of that size and instead propose that we introduce a lint that warns when one could encounter potentially surprising drop behaviour. @oli-obk has volunteered to mentor this issue. He suggested that we produce a warning when a temporary with a Drop impl which isn't borrowed by any match arms has its lifetime extended.