# Syntactic sugar for \`if x { return | break | continue }\`

**URL:** <https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096>\
**Category:** language design\
**Created:** [July 5, 2023, 8:05am UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096 "2023-07-05T08:05:55Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Raphael](https://avatars.discourse-cdn.com/v4/letter/r/edb3f5/32.png) [@Raphael](https://internals.rust-lang.org/u/Raphael)\
**Post date:** [July 5, 2023, 8:05am UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/1 "2023-07-05T08:05:55Z")

</div>

I thought about opening an RFC for this and thought I'd like to hear some feedback first. The core idea is this: Everyone knows code like that, especially in Go:

```rust
f, err := os.Open("filename.ext")
if err != nil {
    log.Fatal(err)
}

```

Or the Rust equivalent:

```rust
let Ok(f) = open("filename.txt) else {
    eprintln("Failed to open file.");
    return None;
}

```

I would say, Rust is halfway there - as long as we talk about a pattern and we don't need the undesired value. Otherwise we are back at match. This unfortunately does not really help in cases, where a different condition is checked.

```rust
if !may_i_do_this {
    return Err(SomeError::Forbidden);
}

```

I propose the following syntax to make this more concise:

```rust
return Err(SomeError::Forbidden) if !may_i_do_this;

```

The since it is basically just reordering, it can be rearranged in the compiler to just be treated as the original code. Also, it would be quite easy to apply this to `break` and `continue` as well:

```rust
// Print 0, 2
for i in 0..4 {
    continue if i == 1;
    println!("{i}");
    break if i == 2;
}

```

To add some clarity on what syntax i think should be allowed:

- `return [<expr>] [if <expr>];`
- `break [<expr>] [if <expr>];`
- `continue [if <expr>];`

Edit: Reasons why (taken from my comment): I have a few points for it, lets start with the weakest:

- It reads more naturally: `return` value `if` something reads easier than `if something { return value }` (plus you save the braces
- The most relevant information comes first: If you read `return if` your brain screams "important". When reading `if { ... ` - especially in a method clustered with if's - it is easier to overlook the implication of possibly ending the method right then and there.
- Reduces nesting. This is quite helpful when dealing with a big method, either one where there is a lot of nesting going on already (sure this is bad code in itself but still happens quite commonly) or when a static analysis tool would complain about complexity, even if it is just a method with a lot of different error handling (early returns)
- Formatting: A lot of tools are configured to not allow one line if's. (which imho. makes sense most of the time) With this solution it would not do so because of the missing braces. If you just leave the line breaks, it adds more weight to the previous points.

What do you think of this idea? Is there something that might break? Is it pointless to just save two line breaks and make it slightly more readable? Would you support this?

---

<div class="post-metadata">

**Author:** ![chrisd](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/chrisd/32/7232_2.png) [@chrisd](https://internals.rust-lang.org/u/chrisd)\
**Post date:** [July 5, 2023, 8:16am UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/2 "2023-07-05T08:16:52Z")

</div>

I'm not against it exactly but I'm not clear on the motivation _for_ it. Compare:

```rust
return Err(SomeError::Forbidden) if !may_i_do_this;

```

```rust
if !may_i_do_this { return Err(SomeError::Forbidden); }

```

I'm not clear on what we gain by having two very slightly different ways to do the same thing?

---

<div class="post-metadata">

**Author:** ![Raphael](https://avatars.discourse-cdn.com/v4/letter/r/edb3f5/32.png) [@Raphael](https://internals.rust-lang.org/u/Raphael)\
**Post date:** [July 5, 2023, 8:46am UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/3 "2023-07-05T08:46:51Z")

</div>

I have a few points for it, lets start with the weakest:

- It reads more naturally: `return` value `if` something reads easier than `if something { return value }` (plus you save the braces
- The most relevant information comes first: If you read `return if` your brain screams "important". When reading `if { ... ` - especially in a method clustered with if's - it is easier to overlook the implication of possibly ending the method right then and there.
- Reduces nesting. This is quite helpful when dealing with a big method, either one where there is a lot of nesting going on already (sure this is bad code in itself but still happens quite commonly) or when a static analysis tool would complain about complexity, even if it is just a method with a lot of different error handling (early returns)
- Formatting: A lot of tools are configured to not allow one line if's. (which imho. makes sense most of the time) With this solution it would not do so because of the missing braces. If you just leave the line breaks, it adds more weight to the previous points.

---

<div class="post-metadata">

**Author:** ![farnz](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/farnz/32/6587_2.png) [@farnz](https://internals.rust-lang.org/u/farnz)\
**Post date:** [July 5, 2023, 11:04am UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/4 "2023-07-05T11:04:25Z")

</div>

> [@Raphael](#):
>
> I would say, Rust is halfway there - as long as we talk about a pattern and we don't need the undesired value. Otherwise we are back at match. This unfortunately does not really help in cases, where a different condition is checked.
> 
> ```rust
> if !may_i_do_this {
> return Err(SomeError::Forbidden);
> }
> 
> ```

Specifically for this case, and not considering the other use cases, my experience is that it's usually better to refactor so that `may_i_do_this` is either `Result<(), SomeError>`, or `Result<(), AnError>` where `impl From<AnError> for SomeError` exists, making the code `may_i_do_this?;` instead.

My error enums tend to use [`#[derive(thiserror::Error)]`](https://docs.rs/thiserror/1.0.41/thiserror/), which makes the `impl From<AnError> for SomeError` trivial, and which also allows me to have richer errors:

```rust
#[derive(thiserror::Error)]
pub enum AnError {
    #[error("The user '{0}' is not known")]
    UserUnknown(Username),
    #[error("The safe is on a timer seal until {0}, and all operations are forbidden until it opens")
    TimerSeal(DateTime),
    …
}

#[derive(thiserror::Error)]
pub enum SomeError {
    #[error("Operation forbidden")]
    Forbidden(#[from] AnError),
    …
}

```

This now has a simple "Operation forbidden" error, but also means that I have error context via [`Error::source`](https://doc.rust-lang.org/std/error/trait.Error.html#method.source) if I want to go into details about why the operation was forbidden.

---

<div class="post-metadata">

**Author:** ![Raphael](https://avatars.discourse-cdn.com/v4/letter/r/edb3f5/32.png) [@Raphael](https://internals.rust-lang.org/u/Raphael)\
**Post date:** [July 5, 2023, 12:11pm UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/5 "2023-07-05T12:11:05Z")

</div>

I agree on this, most of the time it is indeed the better solution (and i also use it), though i have an example where this is not a valid solution. I cut the code short for convenience:

```rust
...
let sc = expect!(&parser, Token::Semicolon);
return rc_rep.report_error_missing_semicolon(sc) if !sc;
aggregate!(&parser, Node::Expression, Token::Semicolon);
...

```

Due to the side effects of the error report, yeeting the error (which is lowered to a boolean here for simplicity) after expect. Doing this differently would indeed be possible, but i had no influence on the codebases creation and no one pays us to do such a huge refactor 😛

---

<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:** [July 5, 2023, 1:15pm UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/6 "2023-07-05T13:15:20Z")

</div>

I'm not a fan of such syntax, because the action with a side effect is very easy to see, but the conditional `if` comes later, when I already expect the code to perform the side effect. To me it reads like "do this thing with a side effect, …no wait, actually don't!" It's unnatural and tricky.

Currently in Rust if I see a code like:

```rust
return Err(some expressions here)

```

I can just note that it returns, and not read into the details of the error code if I don't need to. I already know the function diverges right here.

But if this syntax existed, I would have to pay attention to the code following `return Err(…` to see if there's an `if` hiding in there.

Also order of execution of the code is reversed:

```rust
return evaluated_second() if evaluated_first();

```

So reading code top to bottom left to right I can't simulate in my head what's happening, because the thing I read first may or may not happen, so I need to mentally "undo" the action if the condition is false.

---

<div class="post-metadata">

**Author:** ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)\
**Post date:** [July 5, 2023, 2:10pm UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/7 "2023-07-05T14:10:50Z")

</div>

FWIW, Perl has this general syntax form called [statement modifiers](https://perldoc.perl.org/perlsyn#Statement-Modifiers).

---

<div class="post-metadata">

**Author:** ![robinm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/robinm/32/6255_2.png) [@robinm](https://internals.rust-lang.org/u/robinm)\
**Post date:** [July 5, 2023, 2:21pm UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/8 "2023-07-05T14:21:54Z")

</div>

IIRC Ruby also has a similar construct.

---

<div class="post-metadata">

**Author:** ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)\
**Post date:** [July 5, 2023, 4:11pm UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/9 "2023-07-05T16:11:31Z")

</div>

Ah, the Ruby [FAQ](https://www.ruby-lang.org/en/documentation/faq/2/) cites Perl for that:

> Ruby’s syntax and design philosophy are heavily influenced by Perl. [...] Statement modifiers (`if`, `unless`, `while`, `until`, etc.) may appear at the end of any statement.

Wikipedia also lists this as a feature of [BASIC-PLUS](https://en.wikipedia.org/wiki/BASIC-PLUS#Statement_modifiers), which further mentions [JOSS conditions](https://en.wikipedia.org/wiki/JOSS#Loops_and_conditions).

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jrose/32/9591_2.png) [@jrose](https://internals.rust-lang.org/u/jrose)\
**Post date:** [July 5, 2023, 4:46pm UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/10 "2023-07-05T16:46:14Z")

</div>

> [@kornel](#):
>
> So reading code top to bottom left to right I can't simulate in my head what's happening, because the thing I read first may or may not happen, so I need to mentally "undo" the action if the condition is false.

I tend to agree, but I’ll note that that’s also an issue with `let`/`else` (which is part of why the inspiration from Swift spells it `guard let`), or even `let` with an `if` or `match` in the value.

I’ve used the equivalent feature in Ruby and I go back and forth on whether I like it. It’s visually compact and it does read nicely out loud, but it also feels a bit like one of those “gotcha” quizzes that starts with “read all instructions before doing any work” and ends with “ignore instructions 2-10”.

---

<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:** [July 5, 2023, 5:21pm UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/11 "2023-07-05T17:21:17Z")

</div>

I don't think let-else has the same issue: it's logically evaluated in order, it's just that the pattern-match may _fail_. By contrast, with return-if, there's nothing in the return that could _fail_, it's that the condition may cause it to not happen.

---

<div class="post-metadata">

**Author:** ![DenisNavarro](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/denisnavarro/32/11035_2.png) [@DenisNavarro](https://internals.rust-lang.org/u/DenisNavarro)\
**Post date:** [July 5, 2023, 5:27pm UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/12 "2023-07-05T17:27:04Z")

</div>

> [@Raphael](#):
>
> Reasons why (taken from my comment): I have a few points for it, lets start with the weakest:
> 
> - It reads more naturally: `return` value `if` something reads easier than `if something { return value }` (plus you save the braces
> - The most relevant information comes first: If you read `return if` your brain screams "important". When reading `if { ... ` - especially in a method clustered with if's - it is easier to overlook the implication of possibly ending the method right then and there.
> - Reduces nesting. This is quite helpful when dealing with a big method, either one where there is a lot of nesting going on already (sure this is bad code in itself but still happens quite commonly) or when a static analysis tool would complain about complexity, even if it is just a method with a lot of different error handling (early returns)
> - Formatting: A lot of tools are configured to not allow one line if's. (which imho. makes sense most of the time) With this solution it would not do so because of the missing braces. If you just leave the line breaks, it adds more weight to the previous points.

You may be interested in [anyhow::ensure](https://docs.rs/anyhow/1.0.71/anyhow/macro.ensure.html) and [snafu::ensure](https://docs.rs/snafu/0.7.4/snafu/macro.ensure.html).

They don't satisfy your points 1 and 2, but they do satisfy your points 3 and 4.

---

<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:** [July 5, 2023, 6:51pm UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/13 "2023-07-05T18:51:28Z")

</div>

> [@Raphael](#):
>
> `return [<expr>] [if <expr>];`

I'm not a fan of this, since it's not LL(1). If you see `return if`, you don't know which form it's taking, since it could be `return if b { 1 } else { 3 };`.

(Not that Rust is always strictly LL(1), but in general I think it's a good thing to strive for when making new syntax.)

Note that you _can_ already write `<expr> || return <expr>;` if you want.

(You'll get warnings, and I know a bunch of people hate code like that, but it's _possible_.)

---

<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:** [July 5, 2023, 6:58pm UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/14 "2023-07-05T18:58:12Z")

</div>

It's certainly not going to happen now, but things like this are why I was a fan of `unless` instead of `let`-`else`.

Like how we write `assert!(foo)` for "unless foo, panic!()", it would be the same as that for other diverging constructs, like `unless x > 0 { continue }`.

The value of not just writing `if !(x > 0)` is that you know from the `unless` that it's a _guard_ -- if the condition doesn't hold, it's guaranteed that control flow will diverge (like the `else` in `let`-`else` now guarantees). And thus `unless` wouldn't allow an `else` at all.

So you get the "control flow warning" up front from it, without needing to put the expressions in the opposite textual order compared to their evaluation order.

---

<div class="post-metadata">

**Author:** ![RalfJung](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ralfjung/32/2415_2.png) [@RalfJung](https://internals.rust-lang.org/u/RalfJung)\
**Post date:** [July 5, 2023, 7:16pm UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/15 "2023-07-05T19:16:24Z")

</div>

> [@cuviper](#):
>
> FWIW, Perl has this general syntax form called [statement modifiers](https://perldoc.perl.org/perlsyn#Statement-Modifiers).

Yes, and it is IMO one of the biggest obstacles when reading Perl code. Evaluation order should match source code order, everything else is just actively misleading.

Rust has right-to-left evaluation order for `=`, which is bad enough but not sufficient justification for adding more footguns of that sort.

---

<div class="post-metadata">

**Author:** ![VitWW](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/vitww/32/10822_2.png) [@VitWW](https://internals.rust-lang.org/u/VitWW)\
**Post date:** [July 5, 2023, 7:56pm UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/16 "2023-07-05T19:56:34Z")

</div>

How about to write nested `return`s?

```rust
return return Err(SomeError::Forbidden) if !may_i_do_this if !may_i_do_that;

```

Is it clearer?

---

<div class="post-metadata">

**Author:** ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)\
**Post date:** [July 5, 2023, 8:15pm UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/17 "2023-07-05T20:15:02Z")

</div>

> [@scottmcm](#):
>
> Note that you _can_ already write `<expr> || return <expr>;` if you want.
> 
> (You'll get warnings, and I know a bunch of people hate code like that, but it's _possible_.)

You'll get fun errors if you try to chain further expressions too:

```rust
    expr || return || expr2;
    expr || return && expr2;

```

The first tries to return a closure, and the second a `&&bool`.

---

<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:** [July 5, 2023, 8:28pm UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/18 "2023-07-05T20:28:03Z")

</div>

While I don't think in general we need to adhere strictly to fixed lookahead, I do think in this case it's a problem that _human_ lookahead is unbounded. `return 42 if expr` might _look_ obvious at a glance, but `return very_long_expr() if expr`, where `very_long_expr()` may even span multiple lines, makes it look much less obvious.

---

<div class="post-metadata">

**Author:** ![toc](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/toc/32/6692_2.png) [@toc](https://internals.rust-lang.org/u/toc)\
**Post date:** [July 5, 2023, 8:41pm UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/19 "2023-07-05T20:41:23Z")

</div>

While I don't particularly like the proposed syntax, you can write run-on code sentences in regular rust. Some straightforward formatter/linter settings should ask you to convert `return very_long_expr() if expr` to something more readable.

---

<div class="post-metadata">

**Author:** ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)\
**Post date:** [July 5, 2023, 8:41pm UTC](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096/20 "2023-07-05T20:41:39Z")

</div>

You can do this in a macro, if you use a token like `,` from the `:expr` follow set.

```rust
macro_rules! fi {
    ($do:expr, if $cond:expr) => {
        if $cond { $do; }
    };
    ($do:expr, unless $cond:expr) => {
        if !$cond { $do; }
    };
}

fi!(return Err(SomeError::Forbidden), if !may_i_do_this);
fi!(return Err(SomeError::Forbidden), unless may_i_do_this);

```

[Next page](https://internals.rust-lang.org/t/syntactic-sugar-for-if-x-return-break-continue/19096.md?page=2)
