No interaction between let .. else and let chains?

Are there any reasons for let .. else and let chains don't play together?

fn foo(xs: &[usize]) -> usize {
    if let Some(x) = xs.first().copied() && x > 4 {
        x
    } else {
        0
    }
}

fn bar(xs: &[usize]) -> usize {
    let Some(x) = xs.first().copied() && x > 4 else {
        return 0
    };
    x
}

It feels to me that both foo and bar should do more or less the same, yet foo is allowed but bar isn't. It doesn't really matter in this case, but in my example due to borrow checker issues I ended up duplicating some stuff. I tried to look for a ticket but wasn't able to find one...

3 Likes

It is not disallowed, the syntax you present in the second case already means something else.

1 Like

Hmm.... I see it now, that's a shame.

The same parsing issue with && is true of let chains in if let, which is why the let-chains RFC went out of its way to address that case. The only time it'd come up would be something like let true = expr && expr2, or let range = false .. true && true, and people didn't typically do that kind of thing with booleans, so we were able to transition that syntax over for let chains.

The let-else RFC reserved space, syntactically, for the same thing, to allow for considering it in the future. If you write

    let true = false && true else {
        return;
    };

you'll get a compile-time error because we reserved that syntactic space for the possible future extension of let-else to allow chaining. (And because let true = or let false = can always be better written as an if.)

9 Likes

Oh wow, that is surprising. Thanks for the elaboration!

To me the let-else feels like it should admit only one binding while the if-let chain may produce multiple bindings. I fully appreciate that this intuition of mine is less regular than the language design you portray.

1 Like

Your intuition matches mine, and we've had lots of discussion about whether we want to allow chaining in let-else. :slightly_smiling_face:

3 Likes

I believe there's also some work being done on "if-patterns", whichiI think would make let Some(x) if x == "bar" = foo else {...} valid semantically, not so sure about it being parse-able, though.

:+1: to that concern, yeah. We already restrict the expression in a let-else to avoid ambiguity with the else (it can't end in a brace), and if we add if-patterns I'd expect us to do something similar for the pattern in a let-else.

Amusingly enough, I’ve actually found a bug involving this edge case: `let-else` does not respect `macro_rules` `expr` metavariable grouping · Issue #147899 · rust-lang/rust · GitHub