Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Lots of words to say "goto can be good" with nary an example of a good use of goto. Hard to judge the point without a concrete example.


Good uses of `goto` generally fall into the following categories:

* C-specific compiler-independent cleanup. This is completely unnecessary if your language supports proper destructors. Unfortunately many still-used and newly-invented languages are completely insufficient here. GNU C has `__attribute__((cleanup))` but it's kind of clunky.

* `continue` or `break` for an outer loop. One of the few things Java did correctly was supporting this directly, with names. A few languages support break/continue by number which is error-prone (fortunately they don't make the same mistake C did in adding `break`-skew for `switch, though unfortunately that usually means they also give up on efficient multi-way dispatch)

* nontrivial variants of `fallthrough` for a `switch`. This is generally an indicator that you should refactor out an `inline` function, but sometimes that makes the code significantly uglier since most languages don't support local-promoted-dynamic variables.

* computed `goto` in interpreter loops, state machines, etc. There is no viable replacement for this, though if your compiler is capable of inlining through an array of function pointers that helps (last I checked, GCC and Clang both failed very badly).

* coroutine implementations. Efficient coroutines have to be built-in to the language anyway (so they can optimize the existence of locals), but languages that support coroutines tend to abandon sanity all ye that enter here.


Re write:

static int __init init_nfs_fs(void)

line 2046 without goto. See which version you prefer? I don't see goto use as mandatory.

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin...


That's trivial in any language with destructors.


yeah, until your destructor throws...


Ignoring for a moment that kernels generally disable destructors, and that "failed to clean up" is likely to leave your program in an unrecoverable state even without multiple exceptions being involved (think about what an exception means ... "failed to write to stderr" should not be an exception, and you should be doing little enough allocation during destruction that your dtor can manage even if out of memory) ...

Destructors throwing isn't broken, it just requires explicit care from the programmer to figure out which option to use in case of conflicting exceptions:

* If the existing unwind is a non-exception-unwind, there is no conflict. Exceptions thrown from a destructor just work™ and change it to an exception-unwind. Since C++11 you have to explicitly mark the dtor as `noexcept(false)` to indicate that you think you know what you are doing.

* If you're already winding due to an exception and want to preserve it, you can check for it at the start of the dtor, and wrap the dtor body in a try-catch which conditionally rethrows if there wasn't already one.

* If you're already unwinding due to an exception and want to ignore it in favor of the new exception ... doing this in C++ is pretty ugly but it's certainly possible. In order to use bare `throw` for the no-additional-exception case, you have to replace all stack variables with `option`-like wrappers so you can manually dispose them during `catch`. Other solutions exist if you don't care about correctly using bare `throw`.

* Or, you can do the safe thing and say "simultaneously unhandled exceptions means the programmer likely never considered how to handle this; give up"

Note that most languages that use `try-finally` have even bigger problems to deal with, since they have to worry about `return` as well as `throw`. Bugs and inconsistencies are frequent here.


init_nfs_fs() use of multiple gotos starting to look trivial in comparison to that, huh?[1]

[1] or trivially replacing with so many nested if statements.


`init_nfs_fs` doesn't "throw" from any of its destructors though - most destructors don't even want to throw. Note that all the functions called between the `out` labels don't have (or at least don't do anything with) a returned error value.


As mentioned in the "Go To Statement Considered Harmful" paper, goto is good as long as it is bridled. The goto horror stems from unbridled gotos.

Pretty much every language created in the past 50-some-odd years, encompassing effectively every language you will actually use, enforce bridled gotos, so for all practical purposes there is no bad use of goto.


"goto is not an evil code devouring monster" != "goto is good". It's rarely useful. It would be hard to truly remove from most languages that support it.

But there's a reason even in languages that have it, you use it rarely. Structured programming is really good.


"goto" is the only way in Lua to skip to the end of the loop without nesting conditionals, because they still don't have "continue".


Please see my examples below.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: