let-else is still quite a bit more verbose than the ?-based early return. Compare
let Some(foo) = bar else { return };
let foo = bar?;
The former version has 3 extra words and 4 extra glyphs, totalling 22 extra symbols, which add absolutely nothing of value compared to the latter version.
Worse, let-else statements cannot be composed or chained, and need new explicit bindings for each early return. Compare
let matchBody = matchExpr.matchBody else { return };
let arm = matchBody.matchArmList.first() else { return };
let blockExpr = arm.expr.downcast_ref::<RsBlockExpr>() else { return };
with
let blockExpr = matchExpr.matchBody?
.matchArmList.first()?
.expr.downcast_ref::<RsBlockExpr>()?;
The code is an example from the Intellij Rust plugin, appropriately translated from Kotlin into Rust. The code happens inside an infallible function, which attempts to perform a transformation on the specific piece of code, but bails out if it doesn't apply in the provided context or if the code is malformed.
There are plenty of such constructs in that codebase. There are many things that one can do with an AST, but we must always be ready that the code will have an error, or that our transformations simply won't be applicable. Some of those functions could possibly return Option<()>
instead of ()
, but for most that would just complicate the API for no good reason, since it's perfectly normal that some inspections or refactorings won't be applicable and there is nothing to do in that case other than skip them.
Some of those checks could be transformed into nested if-let blocks in Rust, but those are very unwieldy. Some of them could be significantly simplified using if-let chains, but certainly not all of them. It's also generally a good practice, from the code readability PoV, to bail early from a function, instead of deeply nesting conditionals. It keeps down both the nesting level and the cognitive complexity of the functions, since one doesn't need to track a stack of nested conditions in the head or wonder whether they have an else-branch somewhere down below.
Personally I would very much want it to be possible to use ? on options inside of functions which return ()
or bool
.