# RFC idea: let ... else, easy early returns when pattern matching

**URL:** <https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988>\
**Category:** Uncategorized\
**Created:** [September 23, 2019, 8:29am UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988 "2019-09-23T08:29:12Z")\
**Posts on this page:** 16\
**Page:** 2

<div class="post-metadata">

**Author:** ![comex](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/comex/32/2587_2.png) [@comex](https://internals.rust-lang.org/u/comex)\
**Post date:** [September 27, 2019, 6:22am UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/21 "2019-09-27T06:22:18Z")

</div>

There's also this: [https://github.com/rust-lang/rfcs/issues/2616](https://github.com/rust-lang/rfcs/issues/2616)

---

<div class="post-metadata">

**Author:** ![Aloso](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aloso/32/5039_2.png) [@Aloso](https://internals.rust-lang.org/u/Aloso)\
**Post date:** [September 27, 2019, 8:23pm UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/22 "2019-09-27T20:23:16Z")

</div>

That RFC proposes `if !let = ...` and `if let != ...` syntax. However, this is suboptimal, because the `!` is easy to miss when reading code. The proposed `if !let` syntax has different semantics and control flow than the existing `if let`, so the difference should be more visible.

Another possibility would be a context-sensitive keyword:

```rust
let Some(x) = foo or { ... };

```

or no keyword:

```rust
let Some(x) = foo { ... };

```

Personally, I like the original proposal with `else` most.

> The fact that even deciding whether or not it's ambiguous was hard is a red flag to me.

There are _a lot_ of possibilities to write code that is hard to understand. When you write unreadable code, that's _your_ fault, not the fault of the language.

---

<div class="post-metadata">

**Author:** ![H2CO3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/h2co3/32/2849_2.png) [@H2CO3](https://internals.rust-lang.org/u/H2CO3)\
**Post date:** [September 27, 2019, 8:48pm UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/23 "2019-09-27T20:48:58Z")

</div>

> [@Aloso](#):
>
> There are _a lot_ of possibilities to write code that is hard to understand. When you write unreadable code, that's _your_ fault, not the fault of the language.

This is untrue as-is. When a construct by itself and by default looks confusing, that's definitely a design problem. It's not like there aren't better or worse alternatives in syntax… And anyway, by that logic, why introduce something into the language, just to then instruct people not to use it?

---

<div class="post-metadata">

**Author:** ![Aloso](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aloso/32/5039_2.png) [@Aloso](https://internals.rust-lang.org/u/Aloso)\
**Post date:** [September 27, 2019, 8:56pm UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/24 "2019-09-27T20:56:35Z")

</div>

I think you misunderstood me. The following looks ambiguous (at least to a human):

```rust
let Some(var) = if test() { foo() } else { bar() } else { baz() };

```

The following does not:

```rust
let Some(var) = foo() else {
    baz()
};

```

Also, we can make the first expression more readable by adding parentheses.

---

<div class="post-metadata">

**Author:** ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)\
**Post date:** [September 28, 2019, 6:26pm UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/25 "2019-09-28T18:26:59Z")

</div>

Swift has a [`quard` statement](https://docs.swift.org/swift-book/ReferenceManual/Statements.html#ID524). It may be worth copying it verbatim, with an edition if necessary. Rust's `if let` _is_ copied from Swift already.

```swift
    guard condition else {
        statements
    }

```

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [September 29, 2019, 2:03am UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/26 "2019-09-29T02:03:08Z")

</div>

Honestly, I prefer spelling it as `guard let` (and have suggested as such previously).

I think the current position of the language team (or at least Centril) is to see how far extending `let`'s usability as an expression gets us. We're getting `if let $pat = $expr && let $pat = $expr` currently, and there is rough design for making `let` usable more generally as an expression in `if` conditions, including naturally supporting `if !(let $pat = $expr)`.

(Specifically, the case where the `let` "expression" "evaluates" to `false` would be required to diverge, and `$pat` would be bound in the containing scope, i.e. `guard let`.)

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [September 30, 2019, 4:21am UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/27 "2019-09-30T04:21:09Z")

</div>

> [@CAD97](#):
>
> Specifically, the case where the `let` "expression" "evaluates" to `false` would be required to diverge

As I've said before, I rather like `unless` for this, _given the diverging requirement_. I don't like it in other languages where it's just `if!`, but the "block must be `-> !`" gives it a reason to exist.

That allows for things like `unless i > 0 { continue }` as well, since it's useful to set preconditions for a block for values as well as types. (That's what `assert!` does, for example, so it's clearly valuable.)

---

<div class="post-metadata">

**Author:** ![canndrew](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/canndrew/32/1676_2.png) [@canndrew](https://internals.rust-lang.org/u/canndrew)\
**Post date:** [September 30, 2019, 5:07am UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/28 "2019-09-30T05:07:01Z")

</div>

An advantage of `if !let` syntax is that it can be combined with the `if let` syntax. For example:

```rust
if !let Some(x) = foo && let Some(y) = bar {
    // `y` is in scope here
    // this block has to diverge
}
// `x` is in scope here

```

**Edit:** Actually that idea is broken. Nevermind.

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [September 30, 2019, 12:08pm UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/29 "2019-09-30T12:08:16Z")

</div>

Unfortunately, I don't think that can work. What happens when `x = None; y = None;` in that case?

---

<div class="post-metadata">

**Author:** ![160R](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/160r/32/9502_2.png) [@160R](https://internals.rust-lang.org/u/160R)\
**Post date:** [October 2, 2019, 11:27am UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/30 "2019-10-02T11:27:16Z")

</div>

There's nothing wrong if we would only accept `||` and prohibit `let` matching alongside with `!let`.  
This seems to be the "inversion" of principle that if-let-chain uses.

---

<div class="post-metadata">

**Author:** ![ehiggs](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ehiggs/32/3224_2.png) [@ehiggs](https://internals.rust-lang.org/u/ehiggs)\
**Post date:** [October 4, 2019, 8:52am UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/31 "2019-10-04T08:52:21Z")

</div>

`std::ops::Try` is already an option here.

```rust
let thing = an_enum?;

```

If `std::ops::Try` was a bit easier to implement, and made a bit generic, then this could become:

```rust
let thing = an_enum.info_result::<Ok=SomeVariant>.error_chain(|| "an_enum should be of type SomeVariant")?;

```

Which is kind of gross, but I think it's a superior direction than relying on the if/else pattern matching when looking to extend the syntax.

---

<div class="post-metadata">

**Author:** ![160R](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/160r/32/9502_2.png) [@160R](https://internals.rust-lang.org/u/160R)\
**Post date:** [October 5, 2019, 4:57pm UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/32 "2019-10-05T16:57:40Z")

</div>

It would be nice to allow initializing missing bindings in `else` clause instead of diverging e.g.:

```rust
if !let Some((a, b)) = x {
    a = y;
    b = z;
}

```

or

```rust
let Some((a, b)) = x else {
    a = y;
    b = z;
}

```

---

<div class="post-metadata">

**Author:** ![H2CO3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/h2co3/32/2849_2.png) [@H2CO3](https://internals.rust-lang.org/u/H2CO3)\
**Post date:** [October 5, 2019, 7:39pm UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/33 "2019-10-05T19:39:27Z")

</div>

Sounds like the perfect task for `Option::unwrap_or_else()`.

I would definitely _not_ like to see code like that in the wild, it's needlessly cryptic.

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [October 5, 2019, 7:53pm UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/34 "2019-10-05T19:53:36Z")

</div>

What about the case of a large enum with many cases? It's not exactly practical in all cases to provide `as_variant(&self) -> Option<&Variant>` and `into_variant(self) -> Option<Variant>` for every variant, let alone `unwrap_variant(self) -> Variant`, `unwrap_variant_or(self, Variant) -> Variant`, `unwrap_variant_or_default(self) -> Variant`, `unwrap_variant_or_else(self, impl FnOnce() -> Variant) -> Variant`.

`Option` is just used as a standin for large enumerated types, often in the control of an upstream dependency. Saying "just use `Option` combinators" is dismissing the problem as solved for one specific standard type.

The standard pattern is for an enum that expects this kind of manipulation a lot to provide `as_variant`/`into_variant`, and using `Option` combinators on that for non-diverging control flow (or `Try`-diverging via `?`), and in all other cases,

```rust
let (these, real, names) = match it {
    It::Variant(these, transient, names) => (these, transient, names),
    _ => otherwise(),
};

```

I think I agree that a refutable let should enforce diverging; a big (claimed) benefit of Swift's `guard` is that you know that the block is a diverging error handling case. But saying "just use `Option`" is overly reductive.

---

<div class="post-metadata">

**Author:** ![H2CO3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/h2co3/32/2849_2.png) [@H2CO3](https://internals.rust-lang.org/u/H2CO3)\
**Post date:** [October 5, 2019, 8:48pm UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/35 "2019-10-05T20:48:13Z")

</div>

For more complex types I'd be perfectly happy with `if let Variant(v) = expr { v } else { default }`.

(If it came up very frequently, I'd probably write a helper method for it anyway, because it's duplicated logic even with let-else sugar. I like my code to evolve organically; I don't expect people to provide methods for every possible use case upfront, nor do I do that myself.)

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [January 3, 2020, 8:48pm UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/36 "2020-01-03T20:48:18Z")

</div>

Closed on January 3, 2020, 8:48pm UTC

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.

[Previous page](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988.md?page=1)
