I've seen that let chains are since 1.88.0 on stable which is pretty cool, but there is something that I don't quite understand: On the blog they say that this new feature depends on the temporary scope changes that have been made with 2024 Edition and they're linking to the page in the docs where you can see that temporaries for if let expressions are dropped now before the else branch. I try to understand why that's necessary for let chains but I'm not getting it. Could anybody help me out please? I really want to understand it.
You're unlikely to see why it's going to be necessary until you start writing some scope guards such as the one returned by Mutex or RwLock. Simplifying the provided example:
use std::sync::RwLock;
fn deadlock(value: &RwLock<i32>) {
// `value.read()` returns a `RwLockReadGuard`
// which unlocks the `RwLock` for `.write()`
// ONLY once it goes out of scope; however
// (prior to edition 2024) the following
// would place it in the "outer" scope of
// `if` branch - instead of limiting it to
// the inner block of the `if` block itself
if let 5 = *value.read().unwrap() {
println!("value is 5");
}
// which means that here you enter a "deadlock":
// the `.write()` is blocking until the `RwLock` itself
// becomes available for writes; yet that can only happen
// once `RwLockReadGuard` from the above gets dropped;
// which doesn't happen until the end of function -
// thus the "dead" on the "lock" state
else {
let mut v = value.write().unwrap();
*v = 25;
}
}
fn main() {
let ref lock_10 = RwLock::new(10);
deadlock(lock_10);
}
if let Some(b) = a.foo() && let Some(c) = b.bar() && let Some(d) = c.baz() {
ā¦
} else {
ā¦
}
With new scope rules compiler just have toi [try to] match on a, then on b, then on c⦠and if any of these fails ā then temporary objects are dropped and we go the else block.
With old scope rules, however, compiler would have to remember the last expression that fails ā to properly call destructurs only after else block.
That's inefficient and error-prone. Both for the compiler to implement and for the user to reason about.
I think you understand now why the change in drop behavior is desirous for if let chains. But FWIW, I don't think it's strictly necessary to support if let chains. It's "merely" convoluted to implement, hard to avoid bugs, somewhat unintuitive,[1] would be a very sneaky edition dependent difference, cannot confidently get automatically migrated across editions, etc. I.e. -- AFAICT -- it's more that it's just not worth it to support the feature on all editions due to the complications that arise with pre-edition 2024 drop behavior, and the cross-edition hazards.
Yes. Due to this topic I was also diging in several GitHub issues related to it and found tons of discussions about side-effects and implications of this - from a layperson's perspective - tiny change. Therefore I also have to say that I have only respect and admiration for the people which are actually working on this. This is indeed a considerable intellectual performance.