Is using a 'goto' statement bad?
Asked Answered
S

8

68

After doing some reseach on how to break through a secondary loop

while (true) { // Main Loop
   for (int I = 0; I < 15; I++) { // Secondary loop
       // Do Something
       break; // Break main loop?
   }
}

most people recommended to call the 'goto' function
Looking as the following example:

while (true) { // Main Loop
   for (int I = 0; I < 15; I++) { // Secondary Loop
       // Do Something
       goto ContinueOn; // Breaks the main loop
   }
}
ContinueOn:

However; I have often heard that the 'goto' statement is bad practice. The picture below is perfectly illustrating my point: Series found

So

  • How bad is the goto statement really, and why?
  • Is there a more effective way to break the main loop than using the 'goto' statement?
Shawnshawna answered 10/8, 2012 at 16:53 Comment(12)
It's not really bad, its just that over 99% of the time, the other loop constructs can be used to write cleaner, clearer, generally better code.Greatest
I assume you just meant while(true) rather than do while(true)?Marilyn
Something like his: loopVal=true while (loopVal){ for(){ if (){ loopVal=false; break; } } }Erek
Is there a more effective way to break the main loop than using the 'goto' statement? How is this not a legitimate question?Carnay
The goto looks cleaner imo. But then again, I can't make an answer cos It will be downvoted.Convince
Candi, contThis does the opposite of break; Instead of terminating the loop, it immediately loops again, skipping the rest of the code.inue skips over and continues on with the loop you mean break here is a good link to explain the difference cplus.about.com/od/learningc/ss/clessonfive_3.htmWilie
This strikes me as a perfectly reasonable way to bust out of a deeply nested loop. GOTO gets its "great satan" reputation from use with line numbers and unnecessary use in cases where something needed to be in a function. In JS we have labeled loops that make it easy to break out of nested loops at any level. I don't see anything wrong with this although I'd add a comment of explanation to avoid knee-jerk hysteria from devs who don't really understand the aversion to goto. As long as you're not setting the GOTO anywhere but at the end of a loop or an outer loop there is no smell IMO.Wye
If we're going to debate whether or not GOTO is still a bad practice in general, then this is a duplicate. Otherwise, keep the discussion local to his code.Troupe
@Esailija: If you write an answer that goto is perfectly fine and you can make a case based on it's practical usefullness in the specific scenario as outlined by the OP you will not get downvoted. If your only reason is cause it looks cleaner,..you might be right.Gewgaw
@FrançoisWahl I have done so now but I feel it's all wasted effort. Competing against Jon Skeet and all, in a language I'm not familiar with.Convince
@Esailija: I would not see it as competing. You made a valid point in your post and John made a valid point in his post. Personally I may agree with John's approach but that has nothing to do with his points, it is more to do with the fact that I used goto in VB6 (happily and correctly) but in C# never had the need to due to return, break, yields, etc... That doesn't mean goto is wrong. I also find that when writing testable code you end up writing methods which do a focused unit of work, which makes them easy to unit test. Side-effects of goto in some cases could be hard to unit test.Gewgaw
@FrançoisWahl yeah I am referring to cases where the entire nested loop construct is one unit, I would not have any problem with separating truly separate units into their own functions. It would not make any sense to unit test my Inner function without the parent loop for example.Convince
M
56

EDIT:

How bad is the goto statement really, and why?

It depends on the exact situation. I can't remember any time where I found it made the code more readable than refactoring. It also depends on your personal view of readability - some people dislike it more than others, as is clear from the other answers. (As a point of interest, it's widely used in generated code - all of the async/await code in C# 5 is based on effectively a lot of gotos).

The problem is that situations where goto tends to be used tend to be the kind of situations where refactoring aids things anyway - whereas goto sticks with a solution which becomes harder to follow as the code gets more complicated.

Is there a more effective way to break the main loop than using the 'goto' statement?

Absolutely. Extract your method out into a separate function:

while (ProcessValues(...))
{
    // Body left deliberately empty
}

...

private bool ProcessValues()
{
   for (int i = 0; i < 15; i++)
   {
       // Do something
       return false;
   }
   return true;
}

I generally prefer doing this over introducing an extra local variable to keep track of "have I finished" - although that will work too, of course.

Marilyn answered 10/8, 2012 at 16:56 Comment(7)
Seems a little confusing to me, wouldn't be cleaner to use recursion?Mabe
@MarceloAssis What would you be recursing? His loop either returns true / false and you continue based on that. I don't see it confusing at all.Donnelly
I'm not saying it's bad, it's a clever solution, indeed. But I took a lot of seconds to understand.Mabe
Ideally, methods should be reusable. At the very least they should be logically distinct units of work. I don't think this meets either of those qualifications. This solution makes the code harder to read and understand, simply because of an irrational fear of goto statements - a fear that is a relic from the 1970s.Steeplejack
@MgSam: It's absolutely a distinct unit of work: it does one cycle of processing values, and returns whether or not there's more to do. If we had more context, I could certainly give it a better name, but we really don't know what's going on in here. As for being reusable - I have absolutely no problem with extracting private methods which are only called from one other method, when it enhances readability - which I believe it does in this case.Marilyn
I actually think the goto is a more straightforward way of breaking out of the main loop, and I don't see a real downside for using itRounds
@Rounds isn't break better for breaking out of the main loop ?Alvie
S
65

I'm going to strongly disagree with all of the other answers here. The code you present using goto has nothing wrong with it. There is a reason C# has a goto statement, and it is precisely for these types of scenarios which you describe.

goto simply has a negative stigma because in 1970s and prior people would write horrible, completely unmaintainable code where control flow jumped all over the place because of goto. C#'s goto does not even allow transitioning between methods! Yet there is still this irrational stigma against it.

In my opinion, there is absolutely nothing wrong with using a "modern" goto to break out of an inner loop. The "alternatives" people offer always end up being more complicated and harder to read.

Methods are generally supposed to be reusable. Making a whole separate method for the inner part of a loop, that will only ever get called from that one location, and where the method implementation may end up being at some distant location in the source code, is not an improvement.

Steeplejack answered 10/8, 2012 at 18:10 Comment(1)
The stuff in that wiki link is largely inapplicable to C#. As I mentioned, you cannot jump between methods with goto in C#. For a language like C or C++ where you can do these kinds of crazy things with goto, I agree it is dangerous. In C#, it exists largely for the purpose of breaking out of nested loops.Steeplejack
M
56

EDIT:

How bad is the goto statement really, and why?

It depends on the exact situation. I can't remember any time where I found it made the code more readable than refactoring. It also depends on your personal view of readability - some people dislike it more than others, as is clear from the other answers. (As a point of interest, it's widely used in generated code - all of the async/await code in C# 5 is based on effectively a lot of gotos).

The problem is that situations where goto tends to be used tend to be the kind of situations where refactoring aids things anyway - whereas goto sticks with a solution which becomes harder to follow as the code gets more complicated.

Is there a more effective way to break the main loop than using the 'goto' statement?

Absolutely. Extract your method out into a separate function:

while (ProcessValues(...))
{
    // Body left deliberately empty
}

...

private bool ProcessValues()
{
   for (int i = 0; i < 15; i++)
   {
       // Do something
       return false;
   }
   return true;
}

I generally prefer doing this over introducing an extra local variable to keep track of "have I finished" - although that will work too, of course.

Marilyn answered 10/8, 2012 at 16:56 Comment(7)
Seems a little confusing to me, wouldn't be cleaner to use recursion?Mabe
@MarceloAssis What would you be recursing? His loop either returns true / false and you continue based on that. I don't see it confusing at all.Donnelly
I'm not saying it's bad, it's a clever solution, indeed. But I took a lot of seconds to understand.Mabe
Ideally, methods should be reusable. At the very least they should be logically distinct units of work. I don't think this meets either of those qualifications. This solution makes the code harder to read and understand, simply because of an irrational fear of goto statements - a fear that is a relic from the 1970s.Steeplejack
@MgSam: It's absolutely a distinct unit of work: it does one cycle of processing values, and returns whether or not there's more to do. If we had more context, I could certainly give it a better name, but we really don't know what's going on in here. As for being reusable - I have absolutely no problem with extracting private methods which are only called from one other method, when it enhances readability - which I believe it does in this case.Marilyn
I actually think the goto is a more straightforward way of breaking out of the main loop, and I don't see a real downside for using itRounds
@Rounds isn't break better for breaking out of the main loop ?Alvie
C
44

How bad is the goto statement really, and why?

It's really bad for all the normal reasons given. It's prefectly fine when emulating labeled loops in languages that don't support them.

Replacing it with functions will in many cases scatter logic that really should be read as the same unit. This makes it harder to read. Nobody likes to follow a trail of functions that don't really do anything until at the end of the journey, when you have somewhat forgotten where you started from.

Replacing it with booleans and a bunch of additional ifs and breaks is just really clunky and makes it harder to follow real intentions, like any noise.

In java (and javascript), this is perfectly acceptable (labeled loops):

outer: while( true ) {
    for( int i = 0; i < 15; ++i ) {
        break outer;
    }
}

In C#, it looks like the very close equivalent isn't:

while( true ) {
   for (int I = 0; I < 15; I++) { 
       goto outer;
   }
}
outer:;

Because of the word goto, which has a psychological effect of making people drop all their common sense and make them link xkcd regardless of context.

Is there a more effective way to break the main loop than using the 'goto' statement?

In some cases there isn't, which is why the other languages provide labeled loops and C# provides goto. Note that your example is too simple and it makes the work-arounds not look too bad because they're tailored to the example. In fact, I could just as well suggest this:

   for (int I = 0; I < 15; I++) {
       break;
   }

How about this:

int len = 256;
int val = 65536;

for (int i = 0; i < len; i++)
{
    for (int j = 0; j < len; j++)
    {
        if (i + j >= 2 * val)
        {
            goto outer;
        }
        val = val / 2;
    }
}
outer:;

Does this still look good to you:

int len = 256;
int val = 65536;

for (int i = 0; i < len; i++)
{
    if (!Inner(i, ref val, len))
    {
        break;
    }
}

private bool Inner(int i, ref int val, int len)
{
    for (int j = 0; j < len; j++)
    {
        if (i + j >= 2 * val)
        {
            return false;
        }

        val = val / 2;
    }

    return true;
}
Convince answered 11/8, 2012 at 10:0 Comment(1)
+1 for making a valid case comparing goto to return and break. Looking at the possible miss-uses of goto and unwanted side-effects when comparing to the possible number of miss-uses one can do with return or break I can see why goto is not a favourite. However, that is not because there is necessarily something wrong with goto but more that there is to much miss-use of it. I think though your post is making a very valid point.Gewgaw
S
12

I sometimes use "goto" and I found it looks good, like example above;

bool AskRetry(Exception ex)
{
  return MessageBox.Show(you know here...) == DialogResult.Retry;
}

void SomeFuncOrEventInGui()
{
  re:try{ SomeThing(); }
  catch (Exception ex)
  {
    if (AskRetry(ex)) goto re;
    else Mange(ex); // or throw or log.., whatever...
  }
}

I know you can do same thing recursively, but who cares it just works and I use.

Suanne answered 1/12, 2014 at 10:8 Comment(3)
Agreed and up-voted. Many retry loops are easier to read with goto statements. As others have stated, using a bunch of local variables named "successful" or similar is much clunkier than using a goto to retry the operation.Obcordate
UI driven Retry logic tends to make a real mess of a nicely structured method, goto's can really simplify the flow. That said we have a huge code base and the need to use them has only arisen maybe a dozen times. My advice: use them if other constructs don't fit the bill, but question there use, are they really simplifying the code?Panhandle
This is definitely the cleaner way of doing things then having to try and wrap your head around a recursive function. Code should be easy to follow and this is very straight forward.Rounds
B
8

A colleague of mine (who has 15 years+ in firmware programming) and I use goto all the time. However, we only use it to handle exceptions!! For example:

if (setsockopt(sd,...)<0){
goto setsockopt_failed;
}
...

setsockopt_failed:
close(sd);

To my knowledge, this is the best way to handle exceptions in C. I believe, i works for C# too. Also, I'm not the only one who thinks so: Examples of good gotos in C or C++

Biography answered 16/8, 2013 at 9:8 Comment(0)
D
6

To add to the other answers here, apart from breaking out of nested loops, one neat use of goto is simple and clean state machines:

goto EntryState;

EntryState:
//do stuff
if (condition) goto State1;
if (otherCondition) goto State2;
goto EntryState;

State1:
//do stuff
if (condition) goto State2;
goto State1;

State2:
//do stuff
if (condition) goto State1;
goto State2;

This is a pretty clean and readable way to do state machines, and on top it's performance friendly for free! While you could do this without goto, it would actually only make your code less readable. Don't avoid goto, just think a bit before using it.

Drumhead answered 3/8, 2019 at 17:25 Comment(1)
The case of state machines is often overlooked and is a great use case for straight goto statements over other constructs.Kingship
D
-1

Personally I like to think of goto as "goto leads to hell"...and in many respects this is true as it can lead to highly unmaintainable code and really bad practices.

That being said, it is still implemented for a reason, and when used, it should be used sparingly, if NO other solution is available. OO languages lend themselves to not really needing it (much).

The only times I EVER see it used these days is in obfuscation...a goot example of using goto to create code from hell, so people will be deterred from trying to understand it!

Some languages depend on equivalent keywords. For example in x86 assember you have keywords such as JMP (Jump), JE (Jump if Equal) and JZ (Jump if Zero). These are required frequently in assembly language as there is no OO at this level, and there are few other methods for moving around in an application.

AFAIK...stay away from it unless ABSOLUTELY NECESSARY.

Dagon answered 10/8, 2012 at 22:32 Comment(3)
Could you elaborate why "goto leads to hell"? It being used in obfuscation does not mean it itself is obfuscating the code, Correlation != Causation.Drumhead
Conversely, I can make an argument for using goto as it improves the readability of state machines, makes implementing try-catch-retry logic simpler and reduces unnecessary indentation. Dogmatic answers do not help anybody.Mcclees
I think you can tell if someone had a theoretical education in CS between the late 80s and the early 2000s, by how viceral their reaction to goto and sometimes break is. Having maintained Fortran code written in the 70s, I can tell how a project can goto hell with uncontrolled jumps.Merited
K
-1

Here is an example of where I think it's perfectly fine to use goto:

        switch (law.Value)
        {
            case "J":
                remark += "Routinely oppressive and restrictive\r\n";
                goto case "H";
            case "H":
                remark += "Legalized oppressive practices\r\n";
                goto case "G";
            case "G":
                remark += "Severe punishment for petty infractions\r\n";
                goto case "F";
            case "F":
                remark += "All facets of daily life rigidly controlled\r\n";
                goto case "E";
            case "E":
                remark += "Full-fledged police state\r\n";
                goto case "D";
            case "D":
                remark += "Paramilitary law enforcement\r\n";
                goto case "C";
            case "C":
                remark += "Unrestricted invasion of privacy\r\n";
                goto case "B";
            case "B":
                remark += "Rigid control of civilian movement\r\n";
                goto case "A";
            case "A":
                remark += "Weapon possession\r\n";
                goto case "9";
            case "9":
                remark += "Weapons outside home\r\n";
                goto case "8";
Keir answered 26/5, 2023 at 19:6 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Giglio

© 2022 - 2024 — McMap. All rights reserved.