# \[Pre-RFC\] Breaking out from blocks \[already exists; but stalled\]

**URL:** <https://internals.rust-lang.org/t/pre-rfc-breaking-out-from-blocks-already-exists-but-stalled/12136>\
**Category:** language design\
**Created:** [April 11, 2020, 2:21am UTC](https://internals.rust-lang.org/t/pre-rfc-breaking-out-from-blocks-already-exists-but-stalled/12136 "2020-04-11T02:21:18Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![passcod](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/passcod/32/9363_2.png) [@passcod](https://internals.rust-lang.org/u/passcod)\
**Post date:** [April 11, 2020, 2:21am UTC](https://internals.rust-lang.org/t/pre-rfc-breaking-out-from-blocks-already-exists-but-stalled/12136/1 "2020-04-11T02:21:18Z")

</div>

Background: thought of this while reading [yaah's post on try blocks](https://yaah.dev/try-block), then [tweeted about it](https://twitter.com/passcod/status/1248777511766331392). Further background: have wished for something like it several times while writing Rust, but it wasn't defined enough until now.

**Proposal** : introduce a keyword or mechanism (special-cased macro, whatever) to "early exit" at the block level.

This is in symmetry to:

- `return` at the function level
- `break` at the loop level

Some prelims:

1. I have used the imaginary keyword `breakout` for this. This is not intended for serious consideration, don't bikeshed it 😉
2. The label syntax is also probably bikesheddable but I'd like to focus discussion on the concept rather than syntax at first.

**Trivial example** :

```rust
let custom = 'up: {
    let c = get_config();
    if c.all_defaults() { breakout 'up None }
    // more processing
    if c.invalid_condition() { breakout 'up None }
    // even more
    Some(c)
};

```

This could be written today as:

```rust
let config = {
    let c = get_raw_config();
    if c.all_defaults() {
        None
    } else {
        // more processing
        if c.invalid_condition() {
            None
        } else {
            // even more
            Some(c)
        }
    }
};

```

or even:

```rust
let custom = loop {
    let c = get_config();
    if c.all_defaults() { break None; }
    // more processing
    if c.invalid_condition() { break None; }
    // even more
    break Some(c);
};

```

So at first glance, this is a generalisation of the pattern when you don't actually want a loop, saving some syntax / making it more clear. However, notice I used a label on the `breakout` example and not on the `loop` example: that's because _all_ blocks can be broken out of:

```rust
if let Magical(child) = candidate 'schooling: {
    if child.is_squib() { breakout 'schooling }
    child.parents().inform();

    let letter = Hogwarts::introduce(magician);
    Hogwarts::owlery().send(letter)?;

    if child.parents.request_visit {
        let prof = Hogwarts::staff().travel_to(child.residence);
        if child.parents.refuse_offer {
            prof.obliviate(child);
            prof.obliviate(child.parents);
            breakout 'schooling;
        }
    }

    child.go_to::<Alley<Diagon>>().shop(letter.supplies_list())?;
}

```

This in turns desugars to:

```rust
if let Magical(child) = candidate {
    if !child.is_squib() {
        child.parents().inform();

        let letter = Hogwarts::introduce(magician);
        Hogwarts::owlery().send(letter)?;

        if child.parents.request_visit {
            let prof = Hogwarts::staff().travel_to(child.residence);
            if child.parents.refuse_offer {
                prof.obliviate(child);
                prof.obliviate(child.parents);
            } else {
                child.go_to::<Alley<Diagon>>().shop(letter.supplies_list())?;
            }
        } else {
            child.go_to::<Alley<Diagon>>().shop(letter.supplies_list())?;
        }
    }
}

```

Alternatively, this could be broken into a function, and early `return` used, but that has implications for variable scope and type inference etc.

Desugaring to clean Rust gets even more complex with multiple labelled "bare" blocks, but I'll leave that as an exercise to the reader and talk about more block types:

**Unsafe** : pretty much identical as bare blocks above, except the innards are unsafe.

**Async** : this is, I think, a no-go. Happy to be wrong, though. If wrong, it would look like:

```rust
async 'conf: {
    let config = match fs::read_to_string().await {
        Err(e) => { eprintln!("no config: {:?}", e); breakout 'conf; }
        Ok(c) => c
    };
    let config: Config = config.parse()?;
    // etc
}

```

**Try** : this is interesting, especially in the context of debating `throw`/`raise`/`yeet` keyword syntax, as:

```rust
try {
    yeet some_error;
}

```

would become sugar for:

```rust
try {
    breakout Err(some_error);
}

```

and this proposal would also allow early-ok-exit:

```rust
try 'trying: {
    // stuff
    if good { breakout 'trying Ok(success); }
    // more stuff
    breakout Err(fail);
    // etc
}

```

which with only try-blocks, would desugar to:

```rust
try {
    // stuff
    if good {
        success
    } else {
        // more stuff
        yeet fail;
        // etc
    }
}

```

Edit: this already exists, but is stalled: [https://github.com/rust-lang/rust/issues/48594](https://github.com/rust-lang/rust/issues/48594)

Afaik the async interaction hasn't been explored, though.

---

<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:** [April 11, 2020, 2:48am UTC](https://internals.rust-lang.org/t/pre-rfc-breaking-out-from-blocks-already-exists-but-stalled/12136/2 "2020-04-11T02:48:29Z")

</div>

In progress!

> <https://github.com/rust-lang/rfcs/blob/master/text/2046-label-break-value.md>

---

<div class="post-metadata">

**Author:** ![changhe3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/changhe3/32/6627_2.png) [@changhe3](https://internals.rust-lang.org/u/changhe3)\
**Post date:** [April 11, 2020, 11:26am UTC](https://internals.rust-lang.org/t/pre-rfc-breaking-out-from-blocks-already-exists-but-stalled/12136/3 "2020-04-11T11:26:36Z")

</div>

I'd like to bikeshed that `return` should be used instead of `break`, since we are expressing early return from a block, and in most other languages `break` is mostly associated with a for/while loop, not arbitrary block.

---

<div class="post-metadata">

**Author:** ![kennytm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kennytm/32/161_2.png) [@kennytm](https://internals.rust-lang.org/u/kennytm)\
**Post date:** [April 11, 2020, 11:47am UTC](https://internals.rust-lang.org/t/pre-rfc-breaking-out-from-blocks-already-exists-but-stalled/12136/4 "2020-04-11T11:47:40Z")

</div>

Most C-derived languages also allow `break` in a switch statement which isn't a loop.

---

<div class="post-metadata">

**Author:** ![changhe3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/changhe3/32/6627_2.png) [@changhe3](https://internals.rust-lang.org/u/changhe3)\
**Post date:** [April 11, 2020, 1:15pm UTC](https://internals.rust-lang.org/t/pre-rfc-breaking-out-from-blocks-already-exists-but-stalled/12136/5 "2020-04-11T13:15:41Z")

</div>

You are right but that usage is quite niche.

---

<div class="post-metadata">

**Author:** ![Finn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/finn/32/3841_2.png) [@Finn](https://internals.rust-lang.org/u/Finn)\
**Post date:** [April 11, 2020, 2:53pm UTC](https://internals.rust-lang.org/t/pre-rfc-breaking-out-from-blocks-already-exists-but-stalled/12136/6 "2020-04-11T14:53:41Z")

</div>

To me a more substantial point in favor of `return` is the consideration of the contract programming motivation I brought up lastly. Allowing bare `return` from the function inside of the block passes over the postcondition implicitly, what we could prohibit by shadowing. Returning inside of the block could then be enabled by `return 'fn` or say `return 'loophole`, which violates the contract again, but at least in an explicit fashion. A control structure for contract programming may then be provided by a simple macro. I'm not saying it has to be implemented this way, but we would support it in case someone wants to have it quickly without depending on a crate.

A similar argument applies to the error handling after such a block, which may be passed over implicitly in case the types coincide.

Since the label would become redundant in the base case, we would have to decide anew how to introduce the block. I would be content with `'block: {}`, but that's not to everyones taste.

---

<div class="post-metadata">

**Author:** ![jjpe](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jjpe/32/9779_2.png) [@jjpe](https://internals.rust-lang.org/u/jjpe)\
**Post date:** [April 11, 2020, 4:10pm UTC](https://internals.rust-lang.org/t/pre-rfc-breaking-out-from-blocks-already-exists-but-stalled/12136/7 "2020-04-11T16:10:48Z")

</div>

That argument can be trivially flipped 180°: Most PLs associate `return` exclusively with exiting a function-like construct. Furthermore, `break` is associated with exiting **local control flow constructs** and thus it makes more sense to use `break`, simply for reasons of similarity and Principle of Least Surprise.

---

<div class="post-metadata">

**Author:** ![Uther](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/uther/32/6225_2.png) [@Uther](https://internals.rust-lang.org/u/Uther)\
**Post date:** [April 11, 2020, 8:38pm UTC](https://internals.rust-lang.org/t/pre-rfc-breaking-out-from-blocks-already-exists-but-stalled/12136/8 "2020-04-11T20:38:14Z")

</div>

I seem to me it's the opposite. In any language I know, return is never used to break any kind of block, except functions or closures.

While break can be used to break various kinds of blocks (for, while, switch,...), even labeled blocks in Java, JavaScript and Kotlin at least.

---

<div class="post-metadata">

**Author:** ![felix.s](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/felix.s/32/8073_2.png) [@felix.s](https://internals.rust-lang.org/u/felix.s)\
**Post date:** [April 12, 2020, 1:24pm UTC](https://internals.rust-lang.org/t/pre-rfc-breaking-out-from-blocks-already-exists-but-stalled/12136/9 "2020-04-12T13:24:00Z")

</div>

> [@passcod](#):
>
> Background: thought of this while reading [yaah's post on try blocks](https://yaah.dev/try-block)

That link 404s. It should be [https://yaah.dev/try-blocks](https://yaah.dev/try-blocks).

---

<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:** [April 14, 2020, 3:59pm UTC](https://internals.rust-lang.org/t/pre-rfc-breaking-out-from-blocks-already-exists-but-stalled/12136/10 "2020-04-14T15:59:20Z")

</div>

To be consistent with labelled loops, the label should go before the whole expression, not the opening brace:

```rust
'schooling: if let Magical(child) = candidate {

```

The same applies to the `async` and `try` examples.

The RFC could be extended to break out of _any_ expression, not just blocks, example:

```rust
if 'cond: a || b || foo(c || breakout 'cond false) {...}

```

Which is equivalent to

```rust
if 'cond: {
    a || b || foo(c || breakout 'cond false)
}
{...}

```

~~That's pretty useless in most cases, but it might simplify the grammar and make the feature more consistent.~~

EDIT: That was a bad idea. If `'label: E1 || E2` was equivalent to `'label: (E1 || E2)`, then `'label: {...} || E2` would be equivalent to `'label: ({...} || E2)`, which is confusing.

---

<div class="post-metadata">

**Author:** ![eugene2k](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/eugene2k/32/3525_2.png) [@eugene2k](https://internals.rust-lang.org/u/eugene2k)\
**Post date:** [April 17, 2020, 1:26pm UTC](https://internals.rust-lang.org/t/pre-rfc-breaking-out-from-blocks-already-exists-but-stalled/12136/11 "2020-04-17T13:26:29Z")

</div>

> [@passcod](#):
>
> ```rust
> let custom = loop {
> let c = get_config();
> if c.all_defaults() { break None; }
> // more processing
> if c.invalid_condition() { break None; }
> // even more
> break Some(c);
> };
> 
> ```

You can also write it as

```rust
let custom = (|| {
    let c = get_config();
    if c.all_defaults() { return None; }
    // more processing
    if c.invalid_condition() { return None; }
    Some(c)
})();

```

You can place it into a macro to make the code easier to read:

```rust
macro_rules! block {
    ($($t:tt)*) => {
        (|| {$($t)*})();
    }
}

```

---

<div class="post-metadata">

**Author:** ![passcod](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/passcod/32/9363_2.png) [@passcod](https://internals.rust-lang.org/u/passcod)\
**Post date:** [April 17, 2020, 1:38pm UTC](https://internals.rust-lang.org/t/pre-rfc-breaking-out-from-blocks-already-exists-but-stalled/12136/12 "2020-04-17T13:38:18Z")

</div>

Now consider my example but with a `if foo { return bar; }` in the middle. IIFEs are not the answer.

---

<div class="post-metadata">

**Author:** ![eugene2k](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/eugene2k/32/3525_2.png) [@eugene2k](https://internals.rust-lang.org/u/eugene2k)\
**Post date:** [April 17, 2020, 1:50pm UTC](https://internals.rust-lang.org/t/pre-rfc-breaking-out-from-blocks-already-exists-but-stalled/12136/13 "2020-04-17T13:50:47Z")

</div>

I'm not sure I understand. What should `foo` and `bar` represent? Or is that a return from the calling function? The latter is a problem, yes.

---

<div class="post-metadata">

**Author:** ![ohAitch](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ohaitch/32/5779_2.png) [@ohAitch](https://internals.rust-lang.org/u/ohAitch)\
**Post date:** [April 18, 2020, 7:58pm UTC](https://internals.rust-lang.org/t/pre-rfc-breaking-out-from-blocks-already-exists-but-stalled/12136/14 "2020-04-18T19:58:59Z")

</div>

> [@passcod](#):
>
> **Try** : this is interesting, especially in the context of debating `throw` / `raise` / `yeet` keyword syntax, as:
> 
> ```rust
> try {
> yeet some_error;
> }
> 
> ```
> 
> would become sugar for:
> 
> ```rust
> try {
> breakout Err(some_error);
> }
> 
> ```

Do we need syntax for this beyond `Err(some_error)?` as in current use? I can understand the appeal of a keyword, but I expect the general principles behind moving from `box a` to `Box::new(a)` also apply here.

---

<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:** [July 17, 2020, 7:59pm UTC](https://internals.rust-lang.org/t/pre-rfc-breaking-out-from-blocks-already-exists-but-stalled/12136/15 "2020-07-17T19:59:00Z")

</div>

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