Let-else syntax with actual value in diverging block

When I first read about the let-else syntax (RFC 3137) I sort of expected it to work like this:

let a = Some(5);
let Some(b) = a else { 6 };

But the diverging block must resolve to the never type:
expected `!`, found integer.

I know I can just use match arms, unwrap_or and friends. But I think this one would be a more human readable alternative. Is there an intention to extend the syntax allowing the diverging branch to return an actual value? Are there some caveats that I don't see yet?

What do you expect the above snippet to do when a is None? The else arm must be diverging because otherwise there's no value the expression could evaluate to. Not diverging simply doesn't make any sense.

Then I expect b to be 6.

So, this works now:

let a = Some(5);
let Some(b) = a else { panic!() };
assert_eq!(b, 5);

I expected this to work, too:

let a: Option<i64> = None;
let Some(b) = a else { 6 };
assert_eq!(b, 6);

Use a.unwrap_or(6), then. No need for any new/special syntax.

By the way, there can't only be a single named binding in a pattern. There can be arbitrarily many subpatterns with identifiers, and it's not at all clear which one of them should be assigned the else value.

3 Likes

I think let-else statements are more intended for error handling, not to replace if-let-else statements which would be one possible way you could achieve what you want. Citing from the RFC:

let else simplifies some very common error-handling patterns. It is the natural counterpart to if let, just as else is to regular if.

The RFC explicitly states that the else-block must diverge: 3137-let-else - The Rust RFC Book

1 Like

I fully agree there's no need, but that wasn't my question. I'm just curious if there's currently any intention to extend the syntax since it seems to be more human readable.

I'm not sure what you mean. Even if multiple enums encompass different types of values, I don't see the ambiguity.

enum Foo {
  A(i64),
  B(bool),
  C,
}

let a = Foo::B(false);
let Foo::A(x) = a else { 6 };
assert_eq!(x, 6);

No. That's not what it's for. You are confused as to what the purpose of let-else is. Just use Option's combinators.

let x = (Some(42), None);
let (Some(a), Some(b)) = x else { /* what should go here? */ };

It's pretty clear that you can't just put a 2-tuple there, because it doesn't match the shape of the scrutinee. Arbitrarily assigning values to named bindings would be bad for readability.

3 Likes

Thanks, I see how this can become confusing.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.