General named blocks: why don't we have them?

You can emulate an arbitrary break-able expression with a simple macro:

macro_rules! breakable {
    ($lt:lifetime: $expr:expr) => {
        $lt: loop {
            break $lt $expr;
        }
    }
}

breakable! { 'a: break 'a }

(Well, this expression is also continue-able, which is kinda gross...)

I've always been surprised as to why we don't have this (or, don't have this for block-like expressions like blocks and conditionals), since it seems like a straightforward thing to add. I'm not really suggesting we should RFC it, but I'm wondering if this has been considered and rejected, and, if so, why? (I couldn't really find a thread to show this either way...)

2 Likes

I'm not personally aware of any past discussions/proposals like this.

IME, in Java and C++ where "labels" effectively serve this purpose, the only common usage pattern anyone cites is breaking out of multiple nested for loops.

I suspect this is largely unnecessary in Rust because most use cases of nested for loops are better handled with iterators (things like tuples and Iterator::zip() immediately come to mind), and that's why there hasn't been much demand for this feature. But that's just a guess, and I'm not familiar with prior art in any other languages.

1 Like

C++ doesn't have break-with-label; labels only serve as goto targets. =/

Loops aren't really my inspiration here, anyway. When I look at try blocks, I think "man it would be useful to be able to write some short local computation with early returns without having to go to the trouble of creating a closure". try blocks work for the rather constrained "I want to return an impl Try", and don't support any sort of labels.

Isn't this the same thing as

?

It seems to be in a permanent FCP due to lack of consensus.

1 Like

Huh, amazing. What would be necessary to push that over the finish line? I'm guessing "not on the roadmap" is a problem.

1 Like

The problem is that some members of the language team think it's an obvious win and some think it's a horrible idea, so it's in limbo.

My interpretation is that the core debate is between "this is a too-complicated construct and you should always refactor the code any time you get tempted to use this" and "but people clearly find this useful because they do it with a 'a: loop { ... code there ...; break } and it's no worse than the labeled breaks we already have".

Normally the approach would be to collect data on code that could use it, but I'm not sure this can resolve it because some such examples have already been provided, and thus I assume the response to more examples would remain "and those too should be refactored".


To pre-emptively head off a factually-incorrect objection that seems to come up every time this feature gets discussed: No, this isn't goto. It doesn't introduce the "where am I?" problems that Dijkstra raised in Go To Statement Considered Harmful. It cannot introduce irreducible control flow, so the program remains structured. It structurally cannot jump over declarations, so doesn't require a separate pass to check that kind of thing like goto needs in C++. It only leaves scopes, so it combines naturally with RAII and lifetimes.

In fact, this is exactly the same flow control construct as what break, return, and continue do. Indeed, RFCs and the reference will continue use it regardless of whether it can actually be written, as it's the lower-level primitive to which all of those can be desugared.

5 Likes

I think a good way to look at it is to compare it to Go's goto, which cannot jump into variable lifetimes; I think this is roughly dual to how LBV interacts with RAII. It's certainly not the same, since there's no lexical scopes involved, but it's the same idea. I believe Kotlin has a similar mechanism, with the syntax return@label (though I think at this point I'm just repeating old arguments...).

Conversely, I've found situations in C++ where not having labeled break at all is very painful (though I can't share them because they're pretty specific to my work... =/).

I think adding your input to the tracking issue could possibly be useful, especially if it notes some use cases.