# 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:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![JelteF](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jeltef/32/2177_2.png) [@JelteF](https://internals.rust-lang.org/u/JelteF)\
**Post date:** [September 23, 2019, 8:29am UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/1 "2019-09-23T08:29:12Z")

</div>

One of the tools that I miss most to make code easily understandable in Rust code is easy early returns/continues. They are not are not well supported together with pattern matching. This makes code often highly nested. Which breaks the flow of the code for me, because I cannot simply read it from top to bottom anymore.

The most common thing I do to keep code easy to understand in Python, Go and C (languages I develop professionally in) is the following pattern:

```rust
if x < 10 {
    return Error("x should be more than 10")
}
// do something with x

```

The reasons I like this pattern are:

- you do any checking at the start of the function and in all code after that you know that x is fine
- it's very clear that it errors out on that check
- code doesn't get deeply nested.

With a simple if this is possible in Rust, but when pattern matching is involved (which it often is) the best I could come up with is this:

```rust
let thing = if let SomeVariant(thing) = an_enum {
    thing
} else {
    return Error("an_enum should be of type SomeVariant")
} 
// do something with thing

```

This is a pretty verbose syntax for this pattern. That's why I would like to propose a new syntax. Something like this:

```rust
let SomeVariant(thing) = an_enum else {
    return Error("an_enum should be of type SomeVariant")
}
// do something with thing

```

Before opening a full fledged RFC I wanted to get a bit of feedback on this idea. So some questions:

- Am I missing a better way to do this pattern (or to get its advantages)?
- Do you think this would indeed be a useful addition to the language?
- Any better suggestions for the syntax?

---

<div class="post-metadata">

**Author:** ![bill\_myers](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bill_myers/32/4085_2.png) [@bill\_myers](https://internals.rust-lang.org/u/bill_myers)\
**Post date:** [September 23, 2019, 8:39am UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/2 "2019-09-23T08:39:25Z")

</div>

There's an RFC already at [https://github.com/rust-lang/rfcs/pull/1303](https://github.com/rust-lang/rfcs/pull/1303)

---

<div class="post-metadata">

**Author:** ![matklad](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/matklad/32/12266_2.png) [@matklad](https://internals.rust-lang.org/u/matklad)\
**Post date:** [September 23, 2019, 10:06am UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/3 "2019-09-23T10:06:54Z")

</div>

> [@JelteF](#):
>
> With a simple if this is possible in Rust, but when pattern matching is involved (which it often is) the best I could come up with is this:

Often, a match actually reads better than if-let in this case:

```rust
let thing = match an_enum {
    SomeVariant(thing) => thing,
    _ => return Error("an_enum should be of type SomeVariant"),
} 

```

EDIT: rust-analyzer even has an assist to convert if-let to match: [#454](https://github.com/rust-analyzer/rust-analyzer/pull/454).

---

<div class="post-metadata">

**Author:** ![djc](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/djc/32/1592_2.png) [@djc](https://internals.rust-lang.org/u/djc)\
**Post date:** [September 23, 2019, 11:06am UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/4 "2019-09-23T11:06:49Z")

</div>

See also: [[Pre-RFC] Alternative syntax for pattern-matching based branching](https://internals.rust-lang.org/t/pre-rfc-alternative-syntax-for-pattern-matching-based-branching/10452).

---

<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 23, 2019, 9:25pm UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/5 "2019-09-23T21:25:48Z")

</div>

That's actually inferior to what's being proposed here. `let-else` would shorten (somewhat) a common (somewhat) idiom. That other proposal basically just duplicates the complete semantics of `if let` with a different syntax.

(That said, I'm not a fan of present proposal either, because I'm starting to acquire syntactic diabetes from all this sugar. Yet I'd take this `let-else` as a valid ergonomic improvement in contrast with that other thread, because it actually adds some very minor compression and semantics. But exactly duplicating already existing functionality has no objective benefits other than a very personal "I prefer the colon to if let".)

---

<div class="post-metadata">

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

</div>

The question here is. Do you care what "thing" is?

If so, then the `let thing =match ...`makes a lot of sense. if you've gone to the trouble of putting a value in a variant, you're likely to want to use it. Especially after checking it's there.

You're probably gonna want to unwrap it later anyway,

If not, Option and Result already have the is\_err, is\_ok, is\_some and is\_none methods. which handle the case you don't care what the thing is. It's not too hard to add those to a custom enum.

Or even (if you're feeling super hacky.) proc macro up an auto `is_<variant>()` for deriving.

---

<div class="post-metadata">

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

</div>

I like the idea, sadly that syntax will not be possible:

```rust
let () = if false { () } else {
    return true;
};
return false;

```

would lead to an ambiguous parsing (and if it compiled, it would not be clear whether that block would return `true` or `false`).

On the same vein, this time without parsing ambiguity but just not very readable code, we could get:

```rust
let SomeVariant(x) = if true { foo } else { bar } else {
    return "Madness";
};

```

We will need some special syntax to disambiguate,

```rust
let $fallible_pattern:pat := { $matched_expr:expr } else {
    $fail_expr:expr // (of diverging type: !)
};

```

By using a special token (which could be `=?` or `bikeshed_assignment_sigils`), we are allowed to requires braces, which in turn remove the ambiguity of `else`.

---

<div class="post-metadata">

**Author:** ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)\
**Post date:** [September 25, 2019, 7:11pm UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/8 "2019-09-25T19:11:17Z")

</div>

@dhm I don't think that's ambiguous. If there's an open `if`, then `else` has to pair with that `if`. Otherwise, it pairs with `let`.

---

<div class="post-metadata">

**Author:** ![dhm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dhm/32/4879_2.png) [@dhm](https://internals.rust-lang.org/u/dhm)\
**Post date:** [September 25, 2019, 7:14pm UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/9 "2019-09-25T19:14:23Z")

</div>

You can elide `else { () }`, leading to `if` expressions without a trailing `else`. These `if` expressions evaluate to `()`, but the parser has no knowledge of types so it cannot disambiguate based on that.

---

<div class="post-metadata">

**Author:** ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)\
**Post date:** [September 25, 2019, 7:15pm UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/10 "2019-09-25T19:15:57Z")

</div>

I understand, but I'm saying that the parser can just always pair `else` with the innermost unmatched `if` or `let`, unambiguously.

---

<div class="post-metadata">

**Author:** ![dhm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dhm/32/4879_2.png) [@dhm](https://internals.rust-lang.org/u/dhm)\
**Post date:** [September 25, 2019, 7:21pm UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/11 "2019-09-25T19:21:17Z")

</div>

Good point, giving "priority" to completing a dangling `if` expression does solve the ambiguity for the parser, my bad.

---

<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 25, 2019, 7:27pm UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/12 "2019-09-25T19:27:29Z")

</div>

…although it's not at all intuitive or well-readable.

---

<div class="post-metadata">

**Author:** ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)\
**Post date:** [September 25, 2019, 7:31pm UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/13 "2019-09-25T19:31:51Z")

</div>

I don't think it'll come up, in practice. You can't in general omit the `else` on an `if` on the right-hand side of an assignment, unless the expression has type `()`.

Also, I'd recommend a style lint in clippy saying that if you write `let x = if ... else ... else ...;` then you have to put braces around the first `if ... else ...`.

---

<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 25, 2019, 7:59pm UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/14 "2019-09-25T19:59:16Z")

</div>

I'm talking about the whole problem, not only when there are two `else` clauses. The fact that even deciding whether or not it's ambiguous was hard is a red flag to me.

---

<div class="post-metadata">

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

</div>

I do agree that it's not the most readable solution. I'd like to have something a little more readable.

Suggestions welcome for how we can do that without conflicting with existing syntax or introducing any new keywords.

In particular, I'd love to have something that "announces" the presence of the `else` before the RHS expression.

---

<div class="post-metadata">

**Author:** ![djc](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/djc/32/1592_2.png) [@djc](https://internals.rust-lang.org/u/djc)\
**Post date:** [September 26, 2019, 8:06am UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/16 "2019-09-26T08:06:38Z")

</div>

I definitely feel this pain. Here's another proposal (perhaps a bit out there):

```rust
let Some(value) <= foo else {

}

```

- + Doesn't conflict with existing syntax (I think)
- + Doesn't introduce new keywords
- + Distinguishes from other syntax before the RHS
- - Introduces new "operator"
- + Doesn't repeat the newly introduced variable name
- ? Clarifies assignment rather than conditional

---

<div class="post-metadata">

**Author:** ![pcpthm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/pcpthm/32/4952_2.png) [@pcpthm](https://internals.rust-lang.org/u/pcpthm)\
**Post date:** [September 26, 2019, 10:25am UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/17 "2019-09-26T10:25:27Z")

</div>

> [@matklad](#):
>
> Often, a match actually reads better than if-let in this case:
> 
> ```rust
> let thing = match an_enum {
> SomeVariant(thing) => thing,
> _ => return Error("an_enum should be of type SomeVariant"),
> } 
> 
> ```

I like this idiom except that the identifier `thing` has to be written three times. Could we introduce some sugar? I don't have good generic design in mind but for example:

```rust
let thing = match an_enum {
    <= SomeVariant(@),
    _ => return Err("err"),
}

```

By introducing `<= $pat` match arm syntax. "`@`" is a sigil for the "hole" and result value of the match expression is value of that hole if `$pat` is matched.

---

<div class="post-metadata">

**Author:** ![matklad](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/matklad/32/12266_2.png) [@matklad](https://internals.rust-lang.org/u/matklad)\
**Post date:** [September 26, 2019, 10:41am UTC](https://internals.rust-lang.org/t/rfc-idea-let-else-easy-early-returns-when-pattern-matching/10988/18 "2019-09-26T10:41:53Z")

</div>

> [@pcpthm](#):
>
> I like this idiom except that the identifier `thing` has to be written three times. Could we introduce some sugar? I don't have good generic design in mind but for example:

I just use Kotlin/Groovy name `it`: [https://github.com/rust-analyzer/rust-analyzer/blob/870ce4b1a50a07e3a536ab26215804acdfc9ba8a/crates/ra\_assists/src/ast\_editor.rs#L95-L103](https://github.com/rust-analyzer/rust-analyzer/blob/870ce4b1a50a07e3a536ab26215804acdfc9ba8a/crates/ra_assists/src/ast_editor.rs#L95-L103)

---

<div class="post-metadata">

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

</div>

Isn't this a job for a macro, _à la_ snafu::ensure?

```rust
let_ensure!(SomeVariant(thing) = an_enum, "an_enum should be of type SomeVariant");

// Expands to
let thing = if let SomeVariant(thing) = an_enum {
    thing
} else {
    return Error("an_enum should be of type SomeVariant")
} 
// do something with thing

```

---

<div class="post-metadata">

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

</div>

I'd be wary of using the less-than-or-equal operator for this purpose. Note that `<-` is reserved and could be used here.

But if I recall the discussion from way back then, one of the objections was that the following would be valid, but confusing:

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

```

I.e., `else` being used twice. Changing the assignment operator won't fix this.

**Edit:** Just saw @dhm has already brought this up.

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