8 ms·
Coming from assembly language, I always found the anti-goto sentiment rather cute. A beautiful restriction, but ultimately arbitrary. Like writing poetry. Or
by enriquto 5y ago
Coming from assembly language, I always found the anti-goto sentiment rather cute. A beautiful restriction, but ultimately arbitrary. Like writing poetry. Or those novels that do not ever use the letter "e". Why would an otherwise sane person write code with "rep" and without ever using "jmp"?
- mytailorisrich 5y agoIt's not arbitrary. The goal is to improve quality by making the code better structured and easier to follow. For instance, MISRA C rule 15.1 that states that goto should not be used explains: "Unconstrained use of goto can lead to programs that are unstructured and extremely difficult to understand". Indeed, in practice that's often the case.
- zabzonk 5y agoWell, in C you very rarely need it, but in assembler (particularly the 8-bit Z80 and 6502 that I've written raft-loads of code for) you definitely do. Of course, something like a CALL is better, if you can manage it.
- formerly_proven 5y agogoto is the idiomatic way to emulate RAII in C. It's everywhere.
- zabzonk 5y agoPlease explain a little more with an example. Certainly not in my C code, though I a huge fan of RAII in C++.
- panax 5y agoHere is an example with RAII and goto: https://wiki.sei.cmu.edu/confluence/display/c/MEM12-C.+Consider+using+a+goto+chain+when+leaving+a+function+on+error+when+using+and+releasing+resources https://wiki.sei.cmu.edu/confluence/display/c/MEM12-C.+Consi... Btw that is a Cert C recommendation to use goto for RAII! Take a look at the Linux kernel sometime, goto is everywhere there too https://www.kernel.org/doc/html/v4.10/process/coding-style.html#centralized-exiting-of-functions https://www.kernel.org/doc/html/v4.10/process/coding-style.h...
- zabzonk 5y agoNot many of us are kernel developers, or agree whole-heartedly with their practices.
- ahartmetz 5y agoA function that allocates resources in several steps jumps, on error, to the teardown part at the end to deallocate (in reverse order of allocation) only the resources that were allocated. The others are skipped (jumped over). It's very popular in the Linux kernel.
- zabzonk 5y ago> only the resources that were allocated How does it know which were allocated and which were not? If you are talking about memory allocation, then you could simply deallocate all the pointers provided they were initially set to NULL. Freeing a NULL pointer is well-defined in C.
- maccard 5y agoFiles, sockets, mutexes are all examples of resources that aren't memory. Someone posted an example in this comment thread - [0]. [0] https://wiki.sei.cmu.edu/confluence/display/c/MEM12-C.+Consider+using+a+goto+chain+when+leaving+a+function+on+error+when+using+and+releasing+resources https://wiki.sei.cmu.edu/confluence/display/c/MEM12-C.+Consi...
- zabzonk 5y ago> Files, sockets, mutexes are all examples of resources that aren't memory I know - that's why I explicitly mentioned memory.
- ahartmetz 5y agoThe information is in the instruction pointer. You jump to the place that deallocates the last resource that was allocated, followed by the second-to-last etc. Also, free(NULL) is not well-defined in C, and other resource types aren't so easy to check. delete nullptr in C++ is well-defined, but you rarely need it.
- zabzonk 5y ago> free(NULL) is not well-defined in C Oh yes it is: https://stackoverflow.com/questions/1938735/does-freeptr-where-ptr-is-null-corrupt-memory https://stackoverflow.com/questions/1938735/does-freeptr-whe...
- sys_64738 5y agoIt's mostly used for error bailing as nested if can be pretty dense to read and error prone.
- scoutt 5y agoI kind-of agree with parent, but I would define it "subjective" instead of "arbitrary".
- mytailorisrich 5y agoWell, as per my previous comment it's not subjective, either. It's a "best practice" in the correct sense of the term. I.e. in most cases it is both not needed and clearer, more structured code may be written without using it. Of course, as for anything, it is useful in a very few cases, emphasis on "very few".
- scoutt 5y ago> improve quality by making the code better structured and easier to follow "clearer" and "structured" doesn't matter from a compiler point of view. Hence the only ones that can judge "clearer" or "structured" is us, human beings, and that's why I think it's subjective. "goto" is seen as "incorrect" only because of mere subjective reasons: "it's not clear", "it's not structured", "I read in a forum that it's bad", etc. The keyword exists, it works as intended, and it's safe to use when used correctly, as all the things around programming languages. To me, Rust code is a mess and I cannot get used to it. It's not "clearer" even if it's proven to be bug free and all that.
- dnautics 5y agoRust is not proven to be bug free, it's proven to be memory-safe. Rusts compiler will not save you from bugs in business logic; if you can't write lucid rust and the gunning fog of reviewing your code makes it harder to spot a bug it might not be for you.
- mysterydip 5y agoIsn't the key word in that statement "unconstrained"? Goto is a tool like any other that can be abused.
- mytailorisrich 5y agoThat's why there is this rule to constrain it! It is pragmatic, practical, and clear to have a rule not to use it. This forces people to make the effort to structure their code. Now, if it so happens one day that the result is indeed worse than using goto then a conscious decision can be made to make a specific exception. Otherwise, this is the sort of thing that will creep up and cause unless discussions.
- enriquto 5y agoI agree with you; and I keep a printed copy of the MISRA C document on my nightstand for serene reading before sleep (together with the JPL C rules). Yet this point 15.1 does not forbid at all to use goto, just to use goto in an unconstrained way. Thus it is more a warning than a rule. In the same spirit, you can also enjoy Knuth's "structured programming with goto statements" for a clean programming style whose control flow is based on goto.
- panax 5y agoAnd yet we were recently forced to change a lot of code which did use goto in a constrained way(for error handling only), because someone decided to make 15.1 required instead of advisory and take 15.1 too literally (they also made 15.5 no multiple returns required as well) and some of the results were worse than if someone had used goto in an unconstrained way.
- lelanthran 5y ago> For instance, MISRA C rule 15.1 that states that goto should not be used explains: "Unconstrained use of goto can lead to programs that are unstructured and extremely difficult to understand" Sure, but constrained uses are fine, right? So why blanket-ban it?
- metarox 5y agoMISRA doesn't ban it, that rule is only Advisory which pretty much means you can ignore it. Rules 15.2 and 15.3 (Required) define how you can use goto if you decide to use it. Rule 15.2 The goto statement shall jump to a label declared later in the same function (forward gotos only) Rule 15.3 Any label referenced by a goto statement shall be declared in the same block, or in any block enclosing the goto statement (can't jump a scope level lower than current [jumping in an if block from outside] or from 2 identical but separate scopes [from one if to another independent if]) Personally, I like the Linux kernel model for goto which fits these constraints. Reading code like a shopping list and jumping to the proper cleanup point is much easier to read than having 3-4 if else levels and having to scroll the screen to make sure resources are cleaned at all levels, etc. Nothing beats making the mental model of code smaller to keep in my head and readability in my book so I'll take that approach over applying holy war-ladden absolute rules. Note that using gotos also jives well with Rule 15.5 requiring a single exit point in functions (Advisory). Edit: as panax wrote somewhere in this thread, CERT-C rules even recommend it
- lelanthran 5y agoI'm familiar with MISRA (embedded dev), I only wanted to point out that the MISRA rules in no way justifies a blanket ban on gotos. The ban (in the case of C, anyway) is almost always the result of someone's completely subjective opinion that gotos are bad.
- metarox 5y agoI fully concur on that last statement!
- 5y ago
- samhw 5y ago> Or those novels that do not ever use the letter "e". As a note on this, I think the novel you're talking about is Georges Perec's 'La Disparition'. Supposedly the lack of an 'e' is symbolic of the loss of his family in the Holocaust. Anyway, I always thought one of the greatest feats of the English language was the guy who translated the novel to English, also omitting the letter 'e'. Absolutely mindblowing talent.
- mumblemumble 5y ago> A beautiful restriction, but ultimately arbitrary. Like writing poetry. This assessment cuts pretty close to the gist of (my reading of) Dijkstra's famous paper that kicked off the anti-goto sentiment, once you take some time to digest the entirety of the paper and consider the context in which it was being written. I'd also like to throw out there, though, that the "go to" statement he describes is a "go to" statement that functions like the one in programming languages from before 1968. Which is a goto statement that, I'm guessing, very few of us have ever used. Early versions of BASIC - the "20 GOTO 10" variety - are maybe the most well-known example nowadays. If you've spent much time with a Commodore 64 or a 1st-generation IBM PC, you're probably familiar. He wasn't criticizing C's goto. Not just because C hadn't been invented yet. His most lucid, damning criticisms of goto in higher-level languages can't really be leveled against C's goto statement, because C's goto simply doesn't exhibit the features he's criticizing.
- jsmith45 5y agoRight. He was criticizing the version where one subroutine might jump into the middle of another subroutine. That sort of thing can make safely modifying the second subroutine require knowing if any of its labels are used elsewhere in the program. It is basically impossible to reason locally when developing like that, unless you first verify that the only uses are local. (Or have applied strict coding standards such that only labels specifically identified can be used as non-local goto targets, etc.) That sort of thing was extremely normal in say early assembly programming where your code size was so restricted, you needed to reuse code whenever possible. And many early languages brought that sort of capability along with it. I'd expect Dijkstra's feeling on C's goto would probably be like: Probably a good idea to avoid it when the other control structures work well, but barring silliness like trying to code a large program in a single function, using C's goto for cases not well handled by the other control structures is fine, since it is constrained to local use only.
- pajko 5y agoThere's setjmp() and longjmp()to do that :)
- amelius 5y agoGoto can, conceptually, really mess with the semantics of initialization and finalization of local variables.
- zabzonk 5y agoIndeed, which is why C++ forbids (and the compiler checks) such jumps.
- da_big_ghey 5y agoIt is some good point that goto has many helpful uses, it is when people use it for working around when control flow should be re-factored instead that it becomes harmful. Like on many other things it is no issue in moderation, only in excess.
- throw_m239339 5y ago> Coming from assembly language, I always found the anti-goto sentiment rather cute. A beautiful restriction, but ultimately arbitrary. Like writing poetry. Or those novels that do not ever use the letter "e". Why would an otherwise sane person write code with "rep" and without ever using "jmp"? There is no need to use GOTO when a language has functions/procedures and exceptions. Of course it makes sense in Assembly or more rudimentary languages, or in specific cases for speed optimization (getting out of multiple loops). However, I must admit, sometimes I'm sick of the way Go "deals" with errors so I resort to GOTO to manage HTTP errors in my HTTP request handlers.
- mumblemumble 5y agoOne thing a C-like goto has over exceptions is that you can only jump around within a single lexical scope. That restriction is a big win for readability. Exceptions use a dynamic scope to decide where to jump to, so, in the general case, it's a lot harder to understand how they will affect program behavior by simply reading the code. That's a major reason why using exceptions for non-exceptional control flow is widely considered to be evil.
- srott 5y ago> There is no need to use GOTO when a language has functions/procedures and exceptions. ... and defer
- dragonwriter 5y ago> and defer Which “defer”? The go panic/defer pair is, while structured differently, functionally equivalent to exceptions.
- 0xFACEFEED 5y agoYou use exceptions to clean up resources even when there are no errors? Why?
- Jtsummers 5y ago
- heavenlyblue 5y agoEven in assembly there's position-dependent and position-independent code.
- wruza 5y agoAnd jumps relative to cs or ip, which serve both. PIC idea is not related to goto.
- IshKebab 5y agoIt's not at all like that. `goto` leads to hard to follow, bug-prone code. > Why would an otherwise sane person write code with "rep" and without ever using "jmp"? They wouldn't. Nobody is suggesting that you should avoid `jmp` in assembly. I think maybe you missed the point? The whole "avoid goto" thing is talking about higher level languages than assembly that provide proper flow control primitives like `if`, `for`, `switch` and so on.
- efaref 5y agoAvoiding jmp in assembly is a sensible thing to do. jmp may stall the pipeline if the branch predictor is wrong. Better to use cmov when you can. And if we're being silly, you only really need mov[1]. [1]: https://github.com/xoreaxeaxeax/movfuscator https://github.com/xoreaxeaxeax/movfuscator
- IshKebab 5y agoThat's a completely separate architecture-specific micro-optimisation.
- userbinator 5y agoI started with Asm too and probably have much the same thoughts on goto --- HLLs provide if/else and loops to simplify code, so use them when it makes sense, but when the control flow is such that the simplest solution doesn't fit within the constraints of HLL flow structures, then goto makes perfect sense. Many of those who started with HLLs probably don't realise the machine can be far more flexible and powerful beyond their constraints. It's all about using the appropriate level of abstraction.
- jcranmer 5y agoWhat is done is that people have taken the useful cases of goto, categorized them, and use renamed keywords for different cases. In many modern language, there are four keywords that all imply a general feature of goto: break (go to the end of the identified block), continue (go to the increment block of the identified loop), early return (go to the function cleanup code), and throw (go to something complicated). Indeed, with labeled break and labeled continue, you can actually produce almost any arbitrary control flow [1]. The last major use of goto in C is to recreate what many languages solve via some form of destructor, defer statement, with statement, or some other mechanism that has the compiler insert specified cleanup code on all exiting paths from a block. If you use such a mechanism, then it becomes impossible for the programmer to accidentally skip over such cleanup code, whereas a C function using goto might have the programmer write an early return instead of the goto by accident and skip it. And this is why there's a strong anti-goto sentiment: we have better tools [2] to achieve the same ends that are less error prone than goto is. And when you have the better tools available, why shouldn't you ban the worse tools? [1] The one thing you can do with goto that you can't do with labeled break/continue is create irreducible control flow graphs (essentially, a loop with two entry points, thus having no dominator within the loop itself). On the other hand, I'm pretty sure that murdering a programmer who creates an irreducible control flow graph is considered justifiable homicide, so no harm is lost by outlawing it. [2] In languages other than C, though.
- slaymaker1907 5y agoI would disagree with defer being better than goto. Some people prefer it, but I think in a lot of ways it makes your control flow harder to see than just a few cleanup blocks at the end plus goto.
- Jtsummers 5y agoIt's a tradeoff. defer in Go is more like a try/finally statement in other languages, there's a guarantee that the deferred code will run even when there's a panic in the intervening code: handler = open(something); defer close(handler); // something causing a panic Should be the same as: handler = open(something); try { // something causing an exception } finally { close(handler); } This is not necessarily true of goto, you have to be more deliberate and disciplined (and discipline doesn't scale) to ensure that when your code hits an error you go to the cleanup section, if you miss even a single case and return early, you will not do the cleanup. Both defer and finally eliminate that potential error. The interesting thing (to me) about defer is that it promotes making explicit what's implicit in languages like C++ with RAII. In C++ with RAII, handler would be closed up at the end of the lexical scope but it's implicit, Go makes you explicitly state that you intend for it to be closed using a defer. But the defer, stylistically not by requirement, being near the initialization is, arguably, clearer than the goto or the finally statement. It's also less error prone, you can scan the function and see that someone left out a defer close(handler). But if you use a cleanup block at the end (either with try/finally or with gotos) you have to jump back and forth between the top and bottom of the function in order to see that everything has been cleaned up and in the correct order.
- kristopolous 5y agoBringing it more generally, it's a powerful tool that's simple to use. This means it is ripe for abuse by amateurs and fools and from that comes its reputation. It's a shame that dogmas are developed against the use of such things instead of creating a culture of appropriate use. You see it against say php, jquery, and even gof design patterns these days. It's bullshit. Just because some buffoons do stupid things with something powerful it doesn't mean all people that use it are buffoons
- naniwaduni 5y agoGoto aversion, like single-entry/single-exit and everything anyone has ever said about 3rd-to-5th-generation programming languages, addresses a concern that hasn't been relevant in decades and that most modern programmers have no context for whatsoever. The words have straightforward meanings, unfortunately, so they've since been recontextualized into settings where their prescriptions can still be followed, but the rationale doesn't hold up at all.
- Symmetry 5y agoLikewise using local variables instead of global variables. The point of structured programming is that it's less flexible than gotos are. Decreasing the flexibility of programming constructs means that the reader has fewer possibilities to consider and hence makes the result easier to reason about. Beyond structured programming you might have object oriented programming that uses restricted access to the object's members to enforce certain parities on them that make the object's behavior easier to reason about. Or functional programs that restrict the persistent mutable state and make programs easier to reason about in another way.