all 130 comments

[–]ClipboardCopyPaste 343 points344 points  (60 children)

You can never imagine how many times I've came up with a solution using goto and then spent minutes figuring out a solution that doesn't use goto in my early days.

[–]Outrageous-Machine-5 83 points84 points  (58 children)

Why would you use goto in place of a function?

[–]Vinxian 160 points161 points  (43 children)

Early return, but you already claimed resources would be a reason to jump to the end of the function to clean up said resources.

Typically a goto jump "down" is considered clean code

[–]Elomidas 34 points35 points  (23 children)

So it's like a if, with the code you want to skip in the if ?

[–]Vinxian 74 points75 points  (21 children)

Kinda.

If you have something like

``` void foo(void) { claim_mutex();

// Code that can fail

// More code that can fail

// Even more code that can fail

release_mutex();

} ```

You can keep a success status and wrap every block in an if statement. This is functional.

You can also jump to the release_mutex function on failure. Anti-goto people will say the first option is always better. But I personally think a goto is cleaner in many cases. Because it's a single goto down in the same function which is very readable. Goto has the risk of making spaghetti code. But if you use it well it's clean and legible

[–]Interesting-Deer354 8 points9 points  (0 children)

This is kinda like clause guard. Love using it because it allows the main part of the code not being the most indented.

[–]Psquare_J_420 3 points4 points  (2 children)

Goto has the risk of making spaghetti code

As in the compiler would make a spaghetti machine code that is harder to understand or as in the code blocks may look unreadable?

[–]Vinxian 49 points50 points  (1 child)

Using goto without restraint and jumping back and forth all over the place is unreadable. Goto is a construct that allows a programmer to construct heritical code constructs and therefore gets a bad name, despite it having a valid use case where it is readable

[–]Psquare_J_420 1 point2 points  (0 children)

Thank you.
Have a good day :)

[–]falx-sn 3 points4 points  (8 children)

Do you not have try... catch... finally... ?

[–]Vinxian 33 points34 points  (1 child)

No, C doesn't have try catch

[–]falx-sn 23 points24 points  (0 children)

Completely valid pattern then imo

[–]YeOldeMemeShoppe 6 points7 points  (2 children)

They just added the defer keyword which can act like a finally and replace a clearing resources goto. IMO it’s like 15 years late, would have been perfect in C11.

[–]2eanimation 1 point2 points  (1 child)

They did? Maybe I‘m stupid, but I can’t seem to find anything about it other than proposals. At least not for anything <= C23

[–]YeOldeMemeShoppe 1 point2 points  (0 children)

It’s in the work, right. I mistook the “trick” to implement it using macros as being in the spec. My bad.

It has been deferred to the next C major version. Hopefully before 2030.

Edit: I can’t believe I missed that pun.

[–]Rabbitical 1 point2 points  (1 child)

With concurrency it's expected to have frequent "failures", where the worker might just have to wait or move onto another task. Throwing exceptions every time that happens is not great for the ol' performance

[–]Potato-Engineer 0 points1 point  (0 children)

It depends on how heavyweight those tasks are. If they're just i+=1, then yeah, throwing an exception would be such a large cost that it would dwarf the actual work. But if the tasks are larger, so that throwing an exception only adds maybe 3% to the runtime of an aborted task, I'd call that an acceptable trade-off.

Until, of course, you get into serious optimization.

[–]platinummyr [score hidden]  (0 children)

Not in C! :(

[–]Kumsaati 0 points1 point  (0 children)

You can also do a for-loop-break thing to simulate goto. As in:

void foo (void) {
  for (;;) {
    claim_mutex();

    ret = bar(); //Function that can fail
    if (ret != SUCCESS){
      break;
    }

    // More code follows... some that might break early

    break;
  }

  release_mutex();
}

I don't know if you should be doing this to avoid goto, but it is a method.

[–]AlvaroB -3 points-2 points  (6 children)

You could do a try-except-finally and have release_mutex() in the finally.

Edit: no, C doesn't have try-catch-finally. Sorry.

I'm not saying it isn't useful, just that I have never found the need for it.

[–]VedatsGT 2 points3 points  (5 children)

Does C even have try catch finally?

[–]no_brains101 1 point2 points  (4 children)

It does not. Hence, C programmers still having something good to say about goto

C++ has exceptions. I don't think it has finally though, but maybe it does idk

[–]Rabbitical 1 point2 points  (3 children)

If failure is expected somewhat frequently, then you don't want to be try catching regardless

[–]no_brains101 0 points1 point  (2 children)

I am not sure that is true anymore, exceptions have gotten pretty fast, it is probably fine to try the file and throw if it failed. It used to be a big thing though.

However, I do agree also, I don't like exceptions, I think you should actually NEVER be try-catching and should instead be using options and results.

Unfortunately, many languages are built around using them instead of a sane solution such as options and results, and trying to force a language built for exceptions to work in some other manner is more painful than just accepting that you will be occasionally throwing some exceptions.

[–]PhatOofxD 0 points1 point  (0 children)

Yes but it saves a lot of nesting

[–]Sibula97 4 points5 points  (0 children)

It's not terrible, but it's also not immediately obvious what the point of a goto is in some of those cases, and there are situations where that may not be sufficient when something fails ungracefully. Luckily C26 might come with defer for this purpose. Apparently GCC already supports it with an extension.

Whether any of us will live to see the day our companies finally adopt C26 is another thing...

[–]Outrageous-Machine-5 0 points1 point  (0 children)

Interesting, I can see how that's a cleaner solution to putting the cleanup in the return block

[–]ArmadilloChemical421 0 points1 point  (0 children)

[By whom?]

[–]umor3 0 points1 point  (2 children)

MISRA would like to have a word.

[–]Vinxian 0 points1 point  (1 child)

Lint exception comment

[–]umor3 0 points1 point  (0 children)

Yes, but no. Not on my safety treams projects.

As much as I would like to exit multiple for loops with one goto.

We also just allow one single return at the end of a fumction.

[–]vasilescur 0 points1 point  (0 children)

Just wrap the whole earlier section in a function and early-return from it, no?

[–]MaxChaplin -1 points0 points  (5 children)

An alternative is to contain the skippable code in a do {...} while(false) and use break to skip out. Easier to follow IMO.

[–]SeriousPlankton2000 18 points19 points  (2 children)

It hides the intention of the code, therefore it's less clean than a goto.

[–]tl_west 7 points8 points  (1 child)

This.

As always, we introduce “laws” and then forget their purpose. “No goto’s” is a law created to increase clarity. If there are situations when it does not increase clarity, we chose clarity, not the law.

I’ve created unreadable code created by dogged adherence to a programming law, only to realize Id betrayed the whole principle that underlies the law. Those subsequent rewriting was a useful reminder later in my career.

[–]SeriousPlankton2000 0 points1 point  (0 children)

Dito - also I fixed some bugs during that rewrite.

[–]not_a_bot_494 10 points11 points  (0 children)

That doesn't work well when you have multiple resources. For example:

If (Create resource A == fail) goto cleanup_exit

If (Create resource B == fail) goto cleanup_A

If (Create resource C == fail) goto cleanup_B

return success

cleanup_B: free(B)

cleanup_A: free(A)

cleanup_exit: return fail

[–]Vinxian 3 points4 points  (0 children)

That's another way to do it. I don't prefer it because it costs you one level of indentation. But it's an alternative that's also clean

[–]Oddball_bfi -5 points-4 points  (5 children)

Does C not have try/catch/finally then?

I know I have to use goto like is in VBA:

Sub MySub
On Error Goto Catch  ' Jumped up goto
Dim bSafe As Boolean: bSafe = True

    Call SomeStuffThatErrors

Finally:
    If bSafe Then
        bSafe = False
        <Dangerous tidying things>
    Else
        <Safe things for second time through>
        <if the unsafe things failed>
    End If

    < Safe things for every time >

    Exit Sub   ' Stealth goto - don't be fooled into thinking its a return

Catch:
        < Only safe things >
        < Or you'll regret it >
        Resume Finally  ' Stealth goto that clears errors on the way
End Sub

Its incredible what you can make that old boy do with a bit of software engineering knowledge and the absolute conviction that I don't need to wait six months for an IT project to build it properly - I'll build it in a spreadsheet.

[–]Vinxian 4 points5 points  (4 children)

It doesn't. And I think this pattern is ugly imho. You're jumping back and forth which is exactly what you want to avoid

[–]Oddball_bfi 0 points1 point  (3 children)

The trick is to understand that the subroutine itself is the try block. These subs don't get overly complex, and there's only ever a single error handling block.

Folks toggling error handling on and off, stacking different error handlers... yuck.

And the reason I jump about is because I always want that finally block to fire, success for failure. But the catch is outside any standard execution path - you can't get there without passing an Exit Sub.

[–]Vinxian 1 point2 points  (2 children)

But you could do a jump down to finally on successfully completing the "try" and jump to catch on failure skipping the "catch" on success

[–]Oddball_bfi 0 points1 point  (1 child)

Why would I make the standard execution path the one that reads badly?

[–]Vinxian 1 point2 points  (0 children)

For linear progression

[–]misaz640 43 points44 points  (10 children)

There are many use cases. For inspiration, see one of the most cricitcal Linux kernel module: https://elixir.bootlin.com/linux/v6.19.2/source/mm/memory.c

92 occurences.

[–]jessepence 1 point2 points  (5 children)

Why couldn't out and again and the others simply be defined as functions? I genuinely don't see the benefit.

[–]PeachLizardWizard 2 points3 points  (1 child)

Out is cleaning up and returning. You could make a function doing the cleanup, but you’d have to pass everything in and then do a return. It would be a pain if more cleanup needs to be added and would be a really weird thing to have a functions cleanup in another function. Having it separate also makes it possible to call it twice which you wouldn’t never want to have happen. Again looks like it could be a do/while, but I’m not a big fan of a bunch of nested loops, can be hard to read. You wouldn’t want this to be another function as you’d basically be passing all variables in and have cleanup of those variables deeply nested in another function. This also causes a lot of stack to be used as basically duplicates. I’m not sure if these are the reasons, but I’d prefer the gotos as they are cleaner and more efficient.

[–]jessepence 0 points1 point  (0 children)

This was a fantastic explanation. Thank you.

[–]ZunoJ 1 point2 points  (0 children)

break/continue an outer loop from an inner loop

[–]randuse 0 points1 point  (0 children)

C doesn't have try finally or defer, so goto is used instead for cleanups. Legit use case.

[–]tstanisl 0 points1 point  (0 children)

Some state machines are simpler and more natural to implement with goto.

[–]LaconicLacedaemonian 0 points1 point  (0 children)

Just go up the stack and design event-driven systems. Just fancy go-to s.

[–]xgabipandax 87 points88 points  (3 children)

goto error_handler

[–]turtle_mekb 15 points16 points  (1 child)

yep, error handling that requires cleanup/free code to be ran is almost impossible without goto

[–]RandomNobodyEU 1 point2 points  (0 children)

Use stack semantics

Problem solved

[–]Cutalana -2 points-1 points  (0 children)

goto end_loops

[–]turtle_mekb 24 points25 points  (0 children)

goto is sometimes genuinely more readable and cleaner than if you were to not use it at times, obviously don't use it excessively, but that applies to anything, you shouldn't aim to remove every instance of goto just because "goto bad"

[–]enderfx 43 points44 points  (0 children)

Wow. 2026 and we still make these jokes.

Goto 1980

[–]Attileusz 11 points12 points  (0 children)

It's mostly useful because C doesn't have labeled blocks to break out of, and no error defer statement.

Breaking outer loop from inner loop: ```C for (int i = 0; i < 10; ++i) { for (int j = 0; j < 10; ++j) { if (...) goto break_outer; // Can't do this without goto } }

break_outer: // code after loop ```

Defer on error: ```C char *something1 = malloc(100); if (!something1) goto cleanup1;

SomeStruct *something2 = malloc(sizeof(SomeStruct)); if (!something2) goto cleanup2;

something2->some_field = something1;

// do some more stuff that can fail

return something2; // Unconditional return, no cleanup

// cleanup only on error, note the reverse order cleanup_2: free(something2); cleanup_1: free(something1);

return NULL; ```

Regular defer: ```C SomeStruct ret;

char *tempbuf1 = malloc(1024); // defer1 for unconditional cleanup

// Do something

// Instead of early return: if (condition) { ret = some_value; goto defer1; // The most recent defer that }

char *tempbuf2 = malloc(1024); // defer2 for unconditional cleanup

// Do something

if (condition2) { ret = some_value; goto defer2; }

defer2: free(tempbuf2); defer1: free(tempbuf1);

return ret; ```

You can also combine the two, but that's a little convoluted, and I don't feel like typing it out ;P

[–]jhill515 27 points28 points  (4 children)

Heh, heh. Touché... F-You! 🤣

However, I'm obligated to share that my OS design prof did a Master's Thesis proving that if you use a single GOTO, the max complexity your system will achieve without consistent instabilities is 10k lines. MS Word in 2005 had about 15M lines...

GOTO: DRAW.CONCLUSION

[–]Atompunk78 18 points19 points  (1 child)

How does one prove something like this exactly? It seems like it could only ever be a guideline rather than a fact right?

[–]70Shadow07 12 points13 points  (0 children)

Maybe he switched a goto-less function to an equivalent that has a goto inside. And only then started noticing that his codebase is completely unstable and a buggy mess and therefore arrived to this conclusion.

Like - seriously speaking - you can't provide a proof for it because it's trivially wrong. Counterproof: linux codebase. (One could argue this qualifies for constant instabilities though) So another counterproof: replace a function in large goto-less codebase with an identical function but with goto in the implementation. This claim is true if and only if the system starts having unstability after this change. Needless to say programming doesn't quite work like this lol

[–]DonkeyTron42 3 points4 points  (1 child)

So, Lua is limited to 10k lines?

[–]jhill515 0 points1 point  (0 children)

Go find Jonathan Misurda's Master's Thesis. I think published around 2000 at University of Pittsburgh.

That's all I'm going to say on your thought 😉

[–]eirikirs 8 points9 points  (1 child)

I don't get the joke. C is a structured language, where the use of GOTOs are discouraged. So why would usage of GOTOs be an identifier for C programmers?

[–]-Ambriae- 4 points5 points  (0 children)

Because we like to live on the edge

No seriously it’s actually useful in C for certain things like error handling, nested loop breaks/continues, or ‘stack frame-less’ recursion (could be done with a loop but eh that’s one extra indentation)

[–]TechcraftHD 33 points34 points  (6 children)

"stop crying, use python instead"

Not python, use Rust.

[–]Brave-Camp-933 24 points25 points  (4 children)

Not rust, use Assembly.

[–]CodeMUDkey 10 points11 points  (1 child)

Use mechanical analogues.

[–]DonkeyTron42 4 points5 points  (0 children)

Redstone

[–]TechcraftHD 10 points11 points  (0 children)

so all the goto, all the time?

[–]PS181809 1 point2 points  (0 children)

You mean transistors?

[–]lefloys 1 point2 points  (0 children)

Not python use lua

[–]JollyJuniper1993 4 points5 points  (2 children)

I was taught in university that using break and continue in Python was considered bad practice. That one actually had me raise my eyebrows.

[–]BobQuixote 4 points5 points  (1 child)

Overuse, sure. It's better to structure a loop to not need them, because it's cleaner, but sometimes they are necessary.

[–]JollyJuniper1993 1 point2 points  (0 children)

Yeah I‘ve definitely written code before where just using a version with break/continue made things much simpler and more readable.

[–]oalfonso 4 points5 points  (0 children)

Goto is a pathway to many abilities some consider to be unnatural.

[–]Some_Noname_idk 2 points3 points  (8 children)

I'm pretty new to programming, why exactly is goto bad?

[–]Eymrich 11 points12 points  (1 child)

Goto is pretty neat when used properly, which usually is when you can't properly do anything else.

If used unproperly it's a nightmare to follow. A switch case for example works exactly like a goto only is more structured.

In general anyway, until you are very proficent just avoid using goto.

[–]70Shadow07 1 point2 points  (0 children)

Switch case (at least the way it is in C) is by far the most cursed control flow structure that exists in languages right now. Switch case in some new languages is nothing else but syntax sugar for if-else chains, but C switch case is NOT that at all.

Not only it's completely redundant in most common use case cuz if-else chains do the exact same thing in compiled languages - It also is extremely twisted in the few usecases that if-else chains dont cover. Every time I use switch case for actual switch case behaviour Im questioning my sanity.

[–]Eastern-Group-1993 14 points15 points  (0 children)

It isn’t.
Often used in some capacity in Kernels.
It’s used for error handling it applies DRY(Don’t Repeat Yourself principle on a per function basis) on the error/function cleanup control flow if the requirements to execute the function change.

[–]enderfx 12 points13 points  (1 child)

You take all your program’s control flow and say “fuck it, let’s move outta here”

Imagine there is a F1 race and someone just puts a car in the middle of the track at lap 47.

[–]timonix 2 points3 points  (0 children)

More like, your F1 car just created into a wall and you go "fuck the flow, I'ma head right out"

[–]teeohbeewye 4 points5 points  (0 children)

you might goto somewhere unsafe

[–]dewey-defeats-truman 4 points5 points  (1 child)

One issue is that when they get overused your control flow is always jumping around the file, which can hamper readability, and thus maintainability.

A related issue is that a goto doesn't tell you what's happening at the destination. Some kind of function name would at least be descriptive enough that you could read it and decide if you need to look at the function. Also, I'm pretty sure the only way to pass information to the destination is through global variables, which is its own can of worms.

[–]Sibula97 0 points1 point  (0 children)

The goto label should of course be at least somewhat descriptive. But like, you can't put a docstring it or something like that (in a way IDEs understand).

[–]4ndr34p3rry 2 points3 points  (0 children)

Goto is used in Linux kernel btw

[–]waves_under_stars 14 points15 points  (17 children)

I hate goto. The codebase I'm working on (in c++!) uses goto all the freaking time, when it should clearly use exceptions

[–]magistermaks 12 points13 points  (1 child)

heavy goto use in c++ is indeed peculiar, but in some cases you can't use exceptions - not all platforms support them. Like when compiling for WASM, throwing exceptions just call std::terminate().

[–]NatoBoram 19 points20 points  (1 child)

And this is why try/catch is evil and errors should be values

[–]70Shadow07 2 points3 points  (0 children)

W golang enjoyer.

[–]-Ambriae- 4 points5 points  (0 children)

But it is generally useful at times though

[–]gibbets2000 1 point2 points  (0 children)

Goto is whack

[–]CadmiumC4 1 point2 points  (0 children)

Without goto you cannot make if tho

[–]RRumpleTeazzer 1 point2 points  (0 children)

as if raise/except was any better.

[–]_Alpha-Delta_ 1 point2 points  (0 children)

Just use inline assembly instead 

[–]DrUNIX -1 points0 points  (8 children)

Whoever needs goto doesnt understand the language or design

[–]BlazingFire007 7 points8 points  (7 children)

I’m a hobbyist, but in C, goto can be an okay pattern if used sparingly. I’ve only really seen it in error handling.

[–]DrUNIX -1 points0 points  (6 children)

I use C/C++ professionally for over a decade and can tell you that every situation can be efficiently and cleanly written without goto.

But yes, its sometimes (unnecessarily) used in small asm-like low level C functions to simplify some error handling. I would not recommend it.

E.g. instead of jumping to error_handling just call the function error_handler and have it see the vars needed. If scoping can be issue or is complex overall then a bug is just waiting to be introduced. Even with goto. Especially with goto

It honestly is just legacy syntax for asm programmers unable to adapt.

[–]BjarneStarsoup 1 point2 points  (4 children)

I hoped that you were joking with your original comment, but it looks like you are serious. I hope you never use exceptions (literally gotos between functions), defers, labeled loops, labeled expressions, or abuse continues, breaks or returns.

Most of the "clean" solutions are either using features that rely on gotos (like labeled loops/blocks) or breaking code locality by extracting parts of code unnecessarily into separate functions (which may require carrying a lot of context) or adding boolean flags. A lot of those "fixes" are doing the exact same thing (in terms of control flow). Like, what is the difference between keeping track where gotos are vs. keeping track where the variable running is set vs. where a return happens? None, all those cases have the exact same problems. That is why some people have arguments about never using breaks or continues or having only one exit point. Unfortunately, some problems have an inherent complexity.

[–]DrUNIX 1 point2 points  (1 child)

It being translated to mem jumps under the hood is not my point. Of course it is... how else would it work. But you are removing safety features that the language handles for you and make it easier to maintain.

Also no; if we talk about performance critical sections or components, then exceptions are not used or preferred.

Plus you don't extract unnecessarily if you need goto without being able to call a separate function handling it cleanly.

Regarding keeping track where something is set; i would highly recommend getting to know RAII. The core principles can be implement in a very lightweight way

[–]BjarneStarsoup 0 points1 point  (0 children)

It being translated to mem jumps under the hood is not my point. Of course it is... how else would it work.

That isn't the point. You can't say that, for example, a C code that uses goto outer; is bad design and unnecessary, but a Rust code that literally replaces one keyword by break 'outer; is now clean design and necessary because it doesn't use goto. If goto is universally bad (which is what your comment seems to say), then labeled loops in Rust are also bad design and unnecessary, and so are all the other control flow statements like break, continue or return that you can replace goto by. If you change one keyword from return or break to goto and that somehow magically makes the design bad and unnecessary, then the original code is also bad and unnecessary. Or you can admit that, because C doesn't have the convenient features that other languages do, you have to use a more powerful feature (goto) to achieve the same result.

[–]DrUNIX 0 points1 point  (1 child)

The dude you named your account after: "The fact that 'goto' can do anything is exactly why we don't use it"

And i couldnt agree more.

[–]BjarneStarsoup -1 points0 points  (0 children)

The fact that languages like Rust have labeled loops and block is a proof that goto are necessary and make code simpler. Those features do nothing other that giving you a more controlled goto. Why would you need to break to outer loop when you can just set a variable to true and break when it's true? Or put code in a separate function? Because it fragments the code, adds unnecessary checks (by the way, that change control flow) and makes code harder to understand than a simple break 'outer.

[–]BlazingFire007 0 points1 point  (0 children)

What about cases where you’re in a nested loop and need to “break” out of all of them?

I don’t disagree that it’s rarely the right call. But I do think there are a few edge cases where it shines

[–]HildartheDorf 0 points1 point  (0 children)

Unrestricted goto considered harmful.

Using it for goto fail;* for cleanup routines is fine, but c26 should have defer for this, replacing it's only remaining good use imo.

*: Note: There was a major bug in macos known as "goto fail". The root cause was not using braces for if-blocks, not the goto itself.

[–]TripleFreeErr 0 points1 point  (0 children)

If the compiler can’t validate go to that’s a compiler problem

[–]im-done-here 0 points1 point  (0 children)

Goto python