Noticed the following difference between match
and if
in Rust regarding lifetimes - let's say we have simple struct:
struct Foo;
impl Foo {
is_valid(&self) -> bool { true }
}
and a mutex that holds it:
let foo = tokio::sync::Mutex::new(Foo);
With the following match
the lock exists until the end of the match
construct:
match foo.lock().await.is_valid() {
_ => foo.lock().await.is_valid(), // Deadlock.
};
This will not finish as the lock still holds when the second line is trying to acquire it. However using an if
will release the first lock right after evaluating the bool:
if foo.lock().await.is_valid() {
foo.lock().await.is_valid(); // This is fine.
}
This code finishes with the lock engaged twice. Does this mean match
hangs on the references more than if
? What's the explanation?
I believe this issue will give you the information you're looking for.
In short the Drop trait is triggered after everything in the match statement is resolved. That means that by the time the inner lock is called, the outer one hasn't been freed yet. For the if statement, the Drop trait is called immediately after the condition is checked and before the condition block is called.
Interestingly, this seems to also occur with if let
expressions too. The following code also deadlocks:
struct Foo;
impl Foo {
fn is_valid(&self) -> bool { true }
}
#[tokio::main]
async fn main() {
let foo = tokio::sync::Mutex::new(Foo);
if let true = foo.lock().await.is_valid() {
foo.lock().await.is_valid();
};
}