Does Rust have an alternative for all the valid uses of goto?

With the recent approval of the become keyword letting us explictly perform tail-call optimizations in Rust, I wonder: does rust have an alternative for all the valid uses of goto? What would be missing, otherwise? It seems named blocks, loops, continue, break and become are good alternatives to goto.

Label-Break-Value is the thing, yup. It allows the one good use of goto, aka "leave a particular block". (See https://github.com/rust-lang/rfcs/pull/2046#issuecomment-311230800.)

Couple with your looping construct of choice if you want "backwards goto" -- it's intentional the you can't go "backwards" without a loop construct in Rust.

Previous discussion:

… and probably a lot more I’m missing

3 Likes

Thank you Jules! I'm a bit new to this, so I was wondering, with the become, continue, break, named loops and named blocks, all the use cases of goto should be covered, from my understanding. I was wondering if this is the case. If this is not the right forum, let me know.

I believe that the specific case of computed goto is one Rust (even with become) doesn't fully serve, although I'm not super familiar with the details. Using a match on a temporary theoretically should be control-flow equivalent, but there are significant cases where LLVM fails to optimize such as well as it does a more direct expression of the control flow with computed goto.

My gut feeling thinks this may also have some to do with match exhaustiveness, fallback cases, and Rust telling LLVM to ud2 in impossible scenarios more often then clang does, but that's just vibe based guesswork without any real merit.

2 Likes

My understanding is, precisely, that computed gotos are the reason become was implemented in the language at all.

Yes, the analogy to computed gotos is a tailcall on a function pointer, in Rust a become on a function reference.

A typical example seems to be dispatch tables to implement interpreters, I looked into this blogpost for an example and quickly implemented it. In contrast to the example in the documentation, I did not use a match.

The whole point of a dispatch table is to avoid match/switch/if-else or similar. Perhaps the dispatch table example in the Rust documentation brought you to the conclusion that you need a match for dispatch tables? Then the example should be changed, so it does no longer use a match.

EDIT 2: a fixed version of the example here

EDIT 3: a better version using a raw pointer

EDIT: Sorry, but computed gotos are rarely used in praxis. I think the main reason become will be added to Rust is that there are a lot of use cases for normal tailcalls. But not so many uses for tailcalls on function pointers. That said, I am not a Rust developer.