# Idea: Early returns and irrefutability

**URL:** <https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751>\
**Category:** language design\
**Created:** [April 1, 2019, 3:39pm UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751 "2019-04-01T15:39:28Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![mcy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mcy/32/6512_2.png) [@mcy](https://internals.rust-lang.org/u/mcy)\
**Post date:** [April 1, 2019, 3:39pm UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/1 "2019-04-01T15:39:28Z")

</div>

A common pattern in languages with nulls is

```rust
if (x != nullptr) {
  return expr;
}
x->blah(); // Treat x as nonnull, no safety though!

```

This is really, really useful for readability, because it avoids rightward drift (which `if let Some(_)` tends to make really really bad in long chains of if-lets).

What I want is the following behavior:

```rust
if let None = x {
  return expr
}
let Some(x) = x;

```

In particular, if an `if-let` can only execute if `x` matches a pattern, and its block types as `!`, then that pattern should be “ruled out” from the list of patterns that are needed to make a match irrefutable. The same holds for breakless while-lets, and match arms that type as `!`. For example:

```rust
enum E { A, B, C, D }
let x: E = E::A;
// E::A is ruled out, since the type is `!`.
if let A = x { return }

// No E::A arm is ok, since x match E::A has been ruled 
// out.
match x {
  E::B => panic!(), // Types as `!`, so its pattern is
                    // ruled out in the containing block.
  E::C => {},
  E::D => {},
}

while let E::C = x {
  // no breaks
}
// To reach this line, x cannot be E::C, so that 
// pattern is ruled out.

let E::D = x; // Irrefutable, since other variants have
              // been ruled out.

```

(For silly bonus points, you could type x as `!` if all its variants are ruled out, but this seems kind of like a bad idea…)

I think this _mostly_ only targets `Option` right now; if and when we get negative if let, you might imagine that the following is allowed:

```rust
// Imaginary syntax, do not bikeshed.
if !let Some(..) = x {
  return
}
// Here, *all* patterns except Some are ruled out.
let Some(x) = x;

```

Of course, you might say “why not just use `Option<T>: Try`?” Like, you could, but that doesn’t give you a chance do to processing between the is-None check and the return, and you are limited to returning an `impl Try`. You could also use `Option` combinators, but I’ve found that long chains of combinators and closures are not exactly readable (see: why we have `for-in`, and why Haskell has `do`). Anything more complicated than `x.map(..).unwrap_or()` is almost certainly too clever.

---

<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:** [April 1, 2019, 4:19pm UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/2 "2019-04-01T16:19:51Z")

</div>

This is typestate. See previous discussion here:

> [@Pre-RFC: Allow pattern matching after guard clause](https://internals.rust-lang.org/t/pre-rfc-allow-pattern-matching-after-guard-clause/6238):
>
> I had opened this as an issue on the Rust repo itself [https://github.com/rust-lang/rust/issues/45978](https://github.com/rust-lang/rust/issues/45978) . I’ll post what’s there as of this writing and I’ll let the conversation continue normally from there. Original Post I’ve opened a separate issue for a Rust style guide on [Guard Clause](https://github.com/rust-unofficial/patterns/issues/62). What this issue is about is that Rust currently doesn’t recognize when a path has already been handled by a Guard Clause. I want to do the following: use std::fs::File; let file = File::open("program.cfg"); …

---

<div class="post-metadata">

**Author:** ![mcy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mcy/32/6512_2.png) [@mcy](https://internals.rust-lang.org/u/mcy)\
**Post date:** [April 1, 2019, 5:19pm UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/3 "2019-04-01T17:19:55Z")

</div>

Ah, excellent. I was somewhat surprised that this hadn’t otherwise been thought of.

---

<div class="post-metadata">

**Author:** ![RustyYato](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/rustyyato/32/13627_2.png) [@RustyYato](https://internals.rust-lang.org/u/RustyYato)\
**Post date:** [April 1, 2019, 5:49pm UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/4 "2019-04-01T17:49:40Z")

</div>

Also, you can do this right now

```rust
let x = match x {
    Some(inside) => inside,
    None => return
}

```

---

<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:** [April 1, 2019, 8:57pm UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/5 "2019-04-01T20:57:18Z")

</div>

Because `match` is an expression (see @RustyYato 's answer), and Rust’s control flow analysis is sound and strongly connected with the type system, so using a common, simple, and teachable pattern, this problem is already almost trivially solvable.

---

<div class="post-metadata">

**Author:** ![mcy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mcy/32/6512_2.png) [@mcy](https://internals.rust-lang.org/u/mcy)\
**Post date:** [April 2, 2019, 2:11pm UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/6 "2019-04-02T14:11:08Z")

</div>

Hmm, fair enough. I feel like rust has a serious “rightward drift” problem, though, which I care a lot about solving. I think this requires more thought than “dragging C++ patterns over”, though.

---

<div class="post-metadata">

**Author:** ![hanna-kruppe](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/hanna-kruppe/32/6540_2.png) [@hanna-kruppe](https://internals.rust-lang.org/u/hanna-kruppe)\
**Post date:** [April 2, 2019, 2:39pm UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/7 "2019-04-02T14:39:19Z")

</div>

One doesn’t need to introduce flow typing or other static analyses at all to get rid of the rightward drift. [As previously proposed](https://github.com/rust-lang/rfcs/pull/1303) one could just add new surface syntax that has a different nesting structure. The linked RFC was postponed in 2016, but I feel it’s been long enough to revisit it, and any case I doubt whether a far more invasive language change such as flow typing would fare any better.

---

<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:** [April 2, 2019, 3:06pm UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/8 "2019-04-02T15:06:34Z")

</div>

I’ve written a decent deal on this topic before, both in a [blog post pretending to be a rust2018 post](https://dev.to/cad97/refutable-let-and-rust-in-2018-4l3k) and [here on irlo](https://internals.rust-lang.org/t/pre-pre-rfc-reviving-refutable-let-runoff/6672).

I do like refinement typing / flow typing / smart casts in Kotlin, but in Kotlin it only happens for a) non-nullable from nullable and b) subtype from supertype.

But I honestly think that “guard let”/“refutable let” is the way to go for Rust, making a semantic version of `let pat = match expr { pat => pat, _ => ! }`.

The problem is that it requires introducing a new syntax to do something that is already very possible (using the “match” desugar). It’s a question of how much gain we get for the cost.

And I don’t know whether it’s really worth it. The `match` version _is_ strictly more powerful, and the `_` arm is already required to be appropriately typed, which includes diverging/breaking.

Where it starts becoming a clear win is when you are binding more than one thing in the refutable let. But how much does that actually happen, where you actually have a deep pattern and extract multiple values or bail?

I think it’s worth it to “canonize” the “guard” pattern with some dedicated syntax to encourage people to use that syntax instead of introducing rightward drift. But proving that to people and the several valid arguments against is no simple task.

Not to mention the bikeshed potential which tends to drown out meaningful talk a lot of the time 😅

---

<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:** [April 3, 2019, 8:19am UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/9 "2019-04-03T08:19:23Z")

</div>

This is definitely a pain point for me. Here’s some examples from Quinn:

> <https://github.com/djc/quinn/blob/master/quinn-proto/src/connection.rs#L441>

> <https://github.com/djc/quinn/blob/master/quinn-proto/src/connection.rs#L475>

> <https://github.com/djc/quinn/blob/master/quinn-proto/src/connection.rs#L736>

> <https://github.com/djc/quinn/blob/master/quinn-proto/src/connection.rs#L819>

> <https://github.com/djc/quinn/blob/master/quinn-proto/src/connection.rs#L1949>

> <https://github.com/djc/quinn/blob/master/quinn-proto/src/connection.rs#L1979>

> <https://github.com/djc/quinn/blob/master/quinn-proto/src/connection.rs#L1984>

> <https://github.com/djc/quinn/blob/master/quinn-proto/src/connection.rs#L2001>

> <https://github.com/djc/quinn/blob/master/quinn-proto/src/connection.rs#L2006>

> <https://github.com/djc/quinn/blob/master/quinn-proto/src/connection.rs#L2038>

> <https://github.com/djc/quinn/blob/master/quinn-proto/src/connection.rs#L2079>

> <https://github.com/djc/quinn/blob/master/quinn-proto/src/connection.rs#L2096>

> <https://github.com/djc/quinn/blob/master/quinn-proto/src/connection.rs#L2109>

That’s just one file and only those instances where we used `x` as the variable name, because they were easy to find (which to me also means that one of the pains associated with this problem is having to introduce an intermediate variable name).

---

<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:** [April 3, 2019, 9:50am UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/10 "2019-04-03T09:50:59Z")

</div>

> [@djc](#):
>
> ```rust
> let stream = if let Some(x) = self.streams.get_send_mut(id) {
> x
> } else {
> continue;
> };
> 
> ```

While I do like something more pattern-general, I think these cases could all be done with something like `let stream = self.streams.get_send_mut(id) || continue;` using

> [@Something for coalescing; aka generalized/improved \`or\_else\`](https://internals.rust-lang.org/t/something-for-coalescing-aka-generalized-improved-or-else/9295):
>
> It was noted in the await syntax thread that Option::or\_else mixes poorly with await (and ?): [https://github.com/rust-lang/rust/issues/57640#issuecomment-457518407](https://github.com/rust-lang/rust/issues/57640#issuecomment-457518407) Since one can think of ? as and\_then, would it make sense for there to be some kind of standard macro or operator for or\_else? Like how C# has the [null coalescing operator](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator) ?? (which spawned the or\_else conversation) and SQL has the [COALESCE function](https://docs.microsoft.com/en-us/sql/t-sql/language-elements/coalesce-transact-sql). As I’ve been spending a bunch of time with ops::Try lately, I realized we could do…

---

<div class="post-metadata">

**Author:** ![dodomorandi](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dodomorandi/32/4427_2.png) [@dodomorandi](https://internals.rust-lang.org/u/dodomorandi)\
**Post date:** [April 3, 2019, 10:23am UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/11 "2019-04-03T10:23:02Z")

</div>

What do you think about relying on the unstable `try_trait` and a macro? Something like:

```rust
macro_rules! unwrap_or_else {
    ($e:expr, $op:tt) => {
        if let Ok(x) = $e.into_result() {
            x
        } else {
            $op
        }
    };    
}

```

This can be improved a lot, but I just wanted to show a basic idea. Obviously, we need to stabilize the `Try` trait, at that point we could experiment with different solutions that do not require any changes in the language.

---

<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:** [April 3, 2019, 10:56am UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/12 "2019-04-03T10:56:51Z")

</div>

> [@scottmcm](#):
>
> While I do like something more pattern-general, I think these cases could all be done with something like `let stream = self.streams.get_send_mut(id) || continue;` using

I would like a single-line solution for this pattern, but this usage of `continue` seems to deviate somewhat from what I've seen so far (IIRC it's mostly used as a statement rather than an expression?).

---

<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:** [April 3, 2019, 12:47pm UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/13 "2019-04-03T12:47:34Z")

</div>

In your case it’d be `break` since that’s what the code uses currently.

But yes, all of `return _`, `break`, `break _`, and `continue` are diverging expressions. Effectively they evaluate to `!` and can coerce to any type, so the usage of “`.unwrap_or!`” here does make sense.

---

<div class="post-metadata">

**Author:** ![zackw](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/zackw/32/2071_2.png) [@zackw](https://internals.rust-lang.org/u/zackw)\
**Post date:** [April 3, 2019, 3:13pm UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/14 "2019-04-03T15:13:22Z")

</div>

> [@djc](#):
>
> ```rust
> let ss = if let Some(x) = self.streams.get_send_mut(frame.id) {
> x
> } else {
> continue;
> };
> 
> ```

@RustyYato's `match` form makes this a _little_ more readable IMO:

```rust
let ss = match self.streams.get_send_mut(frame.id) {
    None => continue,
    Some(x) => x
}

```

But a generalization of `?` does seem like it would be even better...

```rust
let ss = self.streams.get_send_mut(frame_id).unwrap_or! {
    continue
};

```

(with a block argument because it's going to do a control transfer)

---

<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:** [April 4, 2019, 12:38am UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/15 "2019-04-04T00:38:32Z")

</div>

> [@dodomorandi](#):
>
> on the unstable `try_trait` and a macro?

That's exactly the macro I linked in the previous post 😛

> [@djc](#):
>
> but this usage of `continue` seems to deviate somewhat from what I’ve seen so far (IIRC it’s mostly used as a statement rather than an expression?).

`continue` and `break` and `return` are absolutely expressions -- you can already do things like `a == b || continue;` today, though I agree that conventions tend to prefer `if`s for that.

---

<div class="post-metadata">

**Author:** ![mcy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mcy/32/6512_2.png) [@mcy](https://internals.rust-lang.org/u/mcy)\
**Post date:** [April 4, 2019, 1:47am UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/16 "2019-04-04T01:47:20Z")

</div>

That is… a scary, valid use of short-circuiting I had not thought of. It is Javascripty in the worstest way, though. .\_.

---

<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:** [April 4, 2019, 2:35am UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/17 "2019-04-04T02:35:14Z")

</div>

> [@mcy](#):
>
> It is Javascripty in the worstest way, though. .\_.

It's Perl5-y, actually -- `or die` is classic, as seen in [open - Perldoc Browser](https://perldoc.perl.org/functions/open.html)

C# seems to have picked it up too. In constructors it's [common](https://gunnarpeipman.com/csharp/throw-expressions/) to do `this.whatever = whatever ?? throw new ArgumentNullException(nameof(whatever));` -- basically it's the same as an `unwrap` or precondition-`assert!`.

---

<div class="post-metadata">

**Author:** ![mcy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mcy/32/6512_2.png) [@mcy](https://internals.rust-lang.org/u/mcy)\
**Post date:** [April 4, 2019, 2:56am UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/18 "2019-04-04T02:56:37Z")

</div>

> [@scottmcm](#):
>
> It’s Perl5-y, actually – `or die` is classic

You say that like it's supposed to make me feel better... =P

I'm mostly unhappy about mixing things that are clearly not bools in with short-circuiting (I write a lot of C++ at work, and `operator bool` is not banned by our style guide...). At least, I don't think of short-circuit operators as control flow, and it's _really wierd_ to someone who doesn't know that `break` (and keywords that desugar to it) type at `!`. Something something there should be a clippy lint for using `break`/`continue`/`return` (and maybe in general expressions that type at `!`) with short-circuit operators.

---

<div class="post-metadata">

**Author:** ![dodomorandi](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dodomorandi/32/4427_2.png) [@dodomorandi](https://internals.rust-lang.org/u/dodomorandi)\
**Post date:** [April 4, 2019, 6:22am UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/19 "2019-04-04T06:22:21Z")

</div>

Ups, I did not check 😅. However, the version you linked is more interesting than my macro (and safe against other “into\_result” methods). Maybe the only problem is that the [coalesce](https://docs.rs/coalesce/0.1.1/coalesce/) crate is already taken 😓.

---

<div class="post-metadata">

**Author:** ![Centril](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/centril/32/3334_2.png) [@Centril](https://internals.rust-lang.org/u/Centril)\
**Post date:** [April 4, 2019, 7:29am UTC](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751/20 "2019-04-04T07:29:15Z")

</div>

> [@mcy](#):
>
> It is Javascripty in the worstest way, though. .\_.

> [@mcy](#):
>
> At least, I don’t think of short-circuit operators as control flow, and it’s _really wierd_ to someone who doesn’t know that `break` (and keywords that desugar to it) type at `!` .

I'm not sure what you mean by "Javascripty", but this is not one of those "wat" moments that arises due to ad-hoc design. `foo || continue` falls out of `continue : !` and `!` coercing to any other type which is consistent with the language overall.

Also, short-circuit operators really should be thought of as control flow and they may desugar into `match`es in the HIR soon.

[Next page](https://internals.rust-lang.org/t/idea-early-returns-and-irrefutability/9751.md?page=2)
