When some control paths would drop a value, all control paths after that point cannot access that variable again. In the following example, it would be an error to access the guard after the if statement. Despite this, Rust doesn't actually drop the value until it goes out of scope. Why not? I can't think of any legitimate reason to keep the object alive, and it seems likely to cause deadlocks (which is how I came across this behavior).
use scopeguard; // 1.2.0
fn func(guard: impl std::fmt::Debug) {
if false {
drop(guard);
}
println!("guard is not yet dropped")
}
fn main() {
let guard = scopeguard::guard((), |_| println!("value dropped"));
func(guard);
}
A related question, Why is this value not dropped after last use? is different because the value is still name-able after the last use. In this question, the value is un-name-able after the if statement (with exceptions shown in the accepted answer).
I can't think of any legitimate reason to keep the object alive
Rust is obeying a consistent specification of semantics, in which dropping depends on going out of scope. While alternative semantics are possible, and could be the subject of future Rust editions, the exact rules of how to drop things early could add complexity and cause confusion. For example, a return
statement would be an exception, because this code works:
fn func(guard: impl std::fmt::Debug) {
if false {
drop(guard);
return;
}
println!("{guard:?} is not yet dropped") // ScopeGuard { value: () } is not yet dropped
}
it seems likely to cause deadlocks
If your code seems especially susceptible to deadlocks, it may benefit from refactoring. Otherwise, just move drop
to after if
or also drop
in else
.