Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have always known that goto is something bad, locked in a basement somewhere never to be seen for good but I ran into a code example today that makes perfect sense to use goto.

I have an IP where I need to check if is within a list of IPs and then proceed with the code, otherwise throw an exception.

<?php

$ip = '192.168.1.5';
$ips = [
    '192.168.1.3',
    '192.168.1.4',
    '192.168.1.5',
];

foreach ($ips as $i) {
    if ($ip === $i) {
        goto allowed;
    }
}

throw new Exception('Not allowed');

allowed:

...

If I don't use goto then I have to use some variable like

$allowed = false;

foreach ($ips as $i) {
    if ($ip === $i) {
        $allowed = true;
        break;
    }
}

if (!$allowed) {
    throw new Exception('Not allowed');
}

My question is what's so bad with goto when it's used for such obvious and imo relevant cases?

share|improve this question
15  
What makes you think you have to use a flag and a strange control flow? If the condition holds, you perform the action and return, otherwise you throw the exception. There might be rare, obscure cases of a useful GOTO, but this isn't one of them. – Kilian Foth 19 hours ago
13  
The preferred approach is factoring out the IP check into its own function. – CodesInChaos 19 hours ago
21  
PHP supports the in_array function, using which you can eliminate the loop, so it becomes if(!in_array($ip, $ips, TRUE)) new Exception('Not allowed'); – CodesInChaos 19 hours ago
9  
@OhadR: I disagree to page 1 of that DrDobbs article (the remaining parts are ok). Return statements as guard clauses can heavily increase readability. Google for "single entry single exit programming" to find some useful discussions about this. – Doc Brown 19 hours ago
7  
Two cents. All the answers basically say the same thing: gotos aren't bad, people with gotos are bad :) Or as Dijkstra said - "unbridled use". If gotos themselves were bad, every computer would be thrashing insensibly, because they all use jumps, which are gotos. – Mike Dunlavey 9 hours ago

10 Answers 10

up vote 28 down vote accepted

GOTO itself is not an immediate problem, it's the implicit state machines that people tend to implement with it. In your case, you want code that checks whether the IP address is in the list of allowed addresses, hence

if (!contains($ips, $ip)) throw new Exception('Not allowed');

so your code wants to check a condition. The algorithm to implement this check should be of no concern here, in the mental space of your main program the check is atomic. That's how it should be.

But if you put the code that does the check into your main program, you lose that. You introduce mutable state, either explicitly:

$list_contains_ip = undef;        # STATE: we don't know yet

foreach ($ips as $i) {
  if ($ip === $i) {
      $list_contains_ip = true;   # STATE: positive
      break;
  }
                                  # STATE: we still don't know yet, huh?
}
                                  # Well, then...
$list_contains_ip = false;        # STATE: negative

if (!$list_contains_ip) {
  throw new Exception('Not allowed');
}

where $list_contains_ip is your only state variable, or implicitly:

                             # STATE: unknown
foreach ($ips as $i) {       # What are we checking here anyway?
  if ($ip === $i) {
    goto allowed;            # STATE: positive
  }
                             # STATE: unknown
}
                             # guess this means STATE: negative
throw new Exception('Not allowed');

allowed:                     # Guess we jumped over the trap door

As you see, there's an undeclared state variable in the GOTO construct. That's not a problem per se, but these state variables are like pebbles: carrying one is not hard, carrying a bag full of them will make you sweat. Your code will not stay the same: next month you'll be asked to differentiate between private and public addresses. The month after that, your code will need to support IP ranges. Next year, someone will ask you to support IPv6 addresses. In no time, your code will look like this:

if ($ip =~ /:/) goto IP_V6;
if ($ip =~ /\//) goto IP_RANGE;
if ($ip =~ /^10\./) goto IP_IS_PRIVATE;

foreach ($ips as $i) { ... }

IP_IS_PRIVATE:
   foreach ($ip_priv as $i) { ... }

IP_V6:
   foreach ($ipv6 as $i) { ... }

IP_RANGE:
   # i don't even want to know how you'd implement that

ALLOWED:
   # Wait, is this code even correct?
   # There seems to be a bug in here.

And whoever has to debug that code will curse you and your children.

Dijkstra puts it like this:

The unbridled use of the go to statement has as an immediate consequence that it becomes terribly hard to find a meaningful set of coordinates in which to describe the process progress.

And that's why GOTO is considered harmful.

share|improve this answer
11  
That $list_contains_ip = false; statement seems misplaced – Bergi 11 hours ago
    
A bag full of pebbles is easy, I've got a convenient bag to hold them. :p – whatsisname 3 hours ago

There are some legitimate use cases for GOTO. For example for error handling and cleanup in C or for implementing some forms of state machines. But this is not one of these cases. The second example is more readable IMHO, but even more readable would be to extract the loop to a separate function and then return when you find a match. Even better would be (in pseudocode, I don't know exact syntax):

if (!in_array($ip, $ips)) throw new Exception('Not allowed');

So what is so bad about GOTO's? Structured programming uses functions and control structures to organize the code so the syntactic structure reflects the logical structure. If something is only conditionally executed, it will appear in a conditional statement block. If something is executed in a loop, it will appear in a loop block. GOTO enables you to circumvent the syntactic structure by jumping around arbitrarily, thereby making the code much harder to follow.

Of course if you have no other choice you use GOTO, but if the same effect can be achieved with functions and control structures, it is preferable.

share|improve this answer
3  
@jameslarge it's easier to write good optimizing compilers for "structured" languages. Care to elaborate? As a counter example, the Rust folks introduced MIR, an intermediate representation in the compiler which specifically replaces loops, continues, breaks, and such with gotos, because it's simpler to check and optimize. – 8bittree 12 hours ago
5  
"if you have no other choice you use GOTO" This would be exceedingly rare. I honestly cannot think of a case where you have no other choice, outside of completely ludicrous restrictions preventing refactoring existing code or similar nonsense. – jpmc26 12 hours ago
1  
@8bittree, Hmm..., well, that's what I remember them saying back when structured programming was supposed to be the new Silver Bullet. Maybe they were wrong, or since the people I remember saying it were maintaining this language at the time, and that was way long ago, maybe I'm not remembering it correctly. – james large 12 hours ago
3  
Also, IMO, emulating a goto with a rats nest of flags and conditionals (as in the OP's second example) is much less readable than juts using a goto. – Hurkyl 11 hours ago
3  
@8bittree I believe the compiler optimizations here relate to keeping track of program state. If you can jump to (near) arbitrary locations from (near) arbitrary source locations, the compiler has a much harder time controlling the state, and has to be more cautious in how it optimizes (when reordering statements, writing extra setup/teardown blocks, etc.). Using gotos might have worked for the MIR of Rust due to particulars of how state is constructed there, but it's not a general thing. Most notably, the limited connectivity is already enforced by not exposing gotos to the programmer. – R.M. 9 hours ago

Return, break, continue and throw/catch are all essentially gotos--they all transfer control to another piece of code and could all be implemented with gotos--in fact I did so once in a school project, a PASCAL instructor was saying how much better Pascal was than basic because of the structure...so I had to be contrary...

The most important thing about Software Engineering (I'm going to use this term over Coding to refer to a situation where you are being paid by someone else to create a codebase with others that requires ongoing improvement and maintenance) is making code Readable--getting it to do something is almost secondary. Your code will be written only once but, in most cases, people will spend days and weeks revisiting, improving and fixing it--and every time they (or you) will have to start from scratch and try to remember/figure out your code.

Most of the features that have been added to languages over the years are to make software more maintainable, not easier to write (although some languages go in that direction--they often cause long-term problems... see twitter's history with Ruby on Rails for an example).

Anyway, GOTOs can be nearly as easy to follow at their best (A single goto used in a case like you suggest), and a nightmare when abused--and are very easily abused...

So after dealing with spaghetti nightmares for a few years we just said "No", as a community we are not going to accept this--too many people mess it up if given a little leeway--that's really the only problem with them. You could use them... but even if it's the perfect case, the next guy will assume you are a terrible programmer because you don't understand the history of the community.

Many other structures have been developed just to make your code more comprehendible: Functions, Objects, Scoping, Encapsulation, Comments(!)... as well as patterns like "DRY" (preventing duplication) and "YANGI" (Reducing over-generalization/complication of code)--all really only import for the NEXT guy to read your code (Who will probably be you--after you've forgotten most of what you did in the first place!)

share|improve this answer
    
I'm upvoting this just for the sentence, "The most important thing...is making code Readable; getting it to do something is almost secondary." I would say it just slightly differently; it's not so much making code readable as making code simple. But either way, it is oh-so-true that making it do something is secondary. – Wildcard 3 hours ago

Nothing wrong with goto statements themselves. The wrongs are with some of the people that inappropriately use the statement.

In addition to what JacquesB said (error handling in C), you are using goto to exit a non-nested loop, something that you can do by using break. In this case you better use break.

But if you had a nested loop scenario, then using goto would be more elegant/simpler. For example:

$allowed = false;

foreach ($ips as $i) {
    foreach ($ips2 as $i2) {
        if ($ip === $i && $ip === $i2) {
            $allowed = true;
            goto out;
        }
    }
}

out:

if (!$allowed) {
    throw new Exception('Not allowed');
}

The example above doesn't make sense in your specific problem, because you don't need a nested loop. But I hope that you only see the nested loop part of it.

Bonous point: if your list of IPs is small, your method is fine. But if the list grows, know that your approach has an asymptotic worst run-time complexity of O(n). As your list grows, you may wish to use a different method that achieves O(log n) (such as a tree structure) or O(1) (a hash table with no collisions).

share|improve this answer
5  
I'm not sure about PHP, but many languages will support using break and continue with a number (to break/continue that many layers of loops) or a label (to break/continue the loop with that label), either of which should generally be preferable to using goto for nested loops. – 8bittree 12 hours ago
    
@8bittree good point. I didn't know that PHP's break is that fancy. I only know C's break isn't that fancy. Perl's break (they call it last) also used to be similar to C's (i.e. not fancy as PHP's) but it -too- became fancy after some point. I guess my answer is getting less and less useful as more languages are introducing fancy versions of break :) – caveman 11 hours ago
2  
a break with a depth value wouldn't resolve the issue without the extra variable. A variable in its self that creates an extra level of complexity. – Matthew Whited 10 hours ago

As others have said, the problem isn't with the goto itself; the problem is with how people use goto, and how it can make code harder to understand and maintain.

Assume the following snippet of code:

       i = 4;
label: printf( "%d\n", i );

What value gets printed for i? When does it get printed? Until you account for every instance of goto label in your function, you can't know. The simple presence of that label destroys your ability to debug code by simple inspection. For small functions with one or two branches, not much of a problem. For not-small functions...

Way back in the early '90s we were given a pile of C code that drove a 3d graphical display and told to make it run faster. It was only about 5000 lines of code, but all of it was in main, and the author used about 15 or so gotos branching in both directions. This was bad code to begin with, but the presence of those gotos made it so much worse. It took my co-worker about 2 weeks to puzzle out the flow of control. Even better, those gotos resulted in code so tightly coupled with itself that we could not make any changes without breaking something.

We tried compiling with level 1 optimization, and the compiler ate up all available RAM, then all available swap, and then panicked the system (which probably had nothing to do with the gotos themselves, but I like throwing that anecdote out there).

In the end, we gave the customer two options - let us rewrite the whole thing from scratch, or buy faster hardware.

They bought faster hardware.

Bode's rules for using goto:

  1. Branch forward only;
  2. Do not bypass control structures (i.e., do not branch into the body of an if or for or while statement);

There are cases where a goto is the right answer, but they are rare (breaking out of a deeply nested loop is about the only place I'd use it).

share|improve this answer
    
I like these rules. I still wouldn't use goto even in accordance with these rules—I wouldn't use it at all unless it's the only form of branching available in the language—but I can see that it wouldn't be too much of a nightmare to debug goto's if they were written according to these rules. – Wildcard 3 hours ago

What about:

func foo(){
    foreach ($ips as $i) {
        if ($ip === $i) {
            return;
        }
    }

    throw new Exception('Not allowed');
}

There's a related question about program flow here continue and break with an extended scope.

I think when you feel program flow is getting complex enough to use a goto (which aren't completely verboten, but are generally not advised), the cleanest option often is to extract a method.

Just imho, when chosing between the boolean allowed and a goto I would probably go for the boolean. Also, I don't know your language, but there may be some kind of built-in that makes your foreach loop disappear.

share|improve this answer
    
You replaced a bad style solution by a wrong solution. – Doc Brown 19 hours ago
    
Quick edit history summary, I wrote a stupid solution because my brain wasn't on (as @DocBrown rightly points out), but I've actually made it into an answer now. – Nathan Cooper 10 hours ago

In low level languages GOTO is inevitable. But in high level it should be avoided (in the case the language supports it) because it makes programs more difficult to read.

Everything boils down to making the code more difficult to read. High level languages are supossedt o make code easier to read than low level languages like, say, assembler or C.

GOTO doesn't cause global warming nor it causes poverty in the third world. It just makes code more difficult to read.

Most modern languages have control structures that make GOTO unnecessary. Some like Java don't even have it.

In fact, the term spaguetti code comes from convoluted, difficult to follow code causes by unstructured branching structures.

share|improve this answer
3  
goto can make code eaiser to read. When you created extra variables and nested branches versus a single jump the code is much more difficult to read and understand. – Matthew Whited 10 hours ago
    
@MatthewWhited Perhaps if you have a single goto in a ten line method. But most methods are not ten lines long and most of the times when people use GOTO they don't use it "just this once". – Tulains Córdova 9 hours ago
1  
@MatthewWhited In fact, the term spaguetti code comes from convoluted, difficult to follow code caused by unstructured branching structures. Or is spaguetti code easier to red now? Maybe. Vinyl is returning, why wouldn't GOTO. – Tulains Córdova 9 hours ago
2  
@Tulains I have to believe such bad things exist because people keep complaining about then, but most of the times I see goto come up are not examples of convoluted spaghetti code, but instead fairly straightforward things, often emulating patterns of control flow in languages that don't have the syntax for it: the two most common examples are to break out of multiple loops and the example in the OP where goto is used to implement for-else. – Hurkyl 9 hours ago
1  
Unstructured spaghetti code comes from languages where functions/methods don't exist. goto is also great for things such as exception retry, rollback, state machines. – Matthew Whited 8 hours ago

With the exception of goto, all flow constructs in PHP (and most languages) are scoped hierarchically.

Imagine some code examined through squinted eyes:

a;
foo {
    b;
}
c;

Regardless of what control construct foo is (if, while, etc.), there are only certain allowed orders for a, b, and c.

You could have a-b-c, or a-c, or even a-b-b-b-c. But you could never have b-c or a-b-a-c.

...unless you have goto.

$a = 1;
first:
echo 'a';
if ($a === 1) {
    echo 'b';
    $a = 2;
    goto first;
}
echo 'c'; 

goto (in particular backwards goto) can be troublesome enough that it's best to just leave it alone, and used hierarchical, blocked flow constructs.

gotos have a place, but mostly as micro-optimizations in low-level languages. IMO, there's no good place for it in PHP.


FYI, the example code can be written even better than either of your suggestions.

if(!in_array($ip, $ips, true)) {
    throw new Exception('Not allowed');
}
share|improve this answer

With goto I can write faster code!

True. Don't care.

Goto exists in assembly! They just call it jmp.

True. Don't care.

Goto solves problems more simply.

True. Don't care.

In the hands of a disciplined developer code that uses goto can be easier to read.

True. However, I've been that disciplined coder. I've seen what happens to code over time. Goto starts out fine. Then the urge to reuse code sets in. Fairly soon I find myself at a breakpoint having no damn clue what's going on even after looking at program state. Goto makes it hard to reason about code. We've worked really hard creating while, do while, for, for each switch, subroutines, functions, and more all because doing this stuff with if and goto is hard on the brain.

So no. We don't want to look at goto. Sure it's alive and well in the binary but we don't need to see that in source. In fact, if is starting to look a little shaky.

share|improve this answer
1  
"Goto solves problems more simply." / "True. Don't care." - I'd hate to see your code where you deliberately avoid simple solutions in favour of what is most extensible. (Heard of YAGNI?) – immibis 5 hours ago
    
Actually goto is the more extensible one. It's more powerful and more expressive. It's still a bad idea. Sorry but less is more here. – CandiedOrange 4 hours ago
    
Your "meat" paragraph (second to last) is the best one here. I don't agree that "goto solves problems more simply." And I don't know why if would be looking shaky. (In which language?) – Wildcard 3 hours ago
1  
Although I agree with you, this is a poorly worded answer. "True, don't care" is not a convincing argument for anything. I think your main message is this: although goto is appealing to people who prefer using efficient, powerful tools, it leads to a surprisingly huge amount of wasted time later on, even when care is taken. – Artelius 2 hours ago
1  
@artelius The "true, don't care" theme isn't meant to be convincing. It's meant to illustrate the many points I'll concede to goto and still come down against it. Thus arguing those points with me is pointless. Since that's not my point. Get the point? – CandiedOrange 2 hours ago

I know this is a bit off topic but if for some reason you really don't like variables and foreach and in_array is not powerful enough and you don't want to break the code out into it's own function then array_reduce to the rescue:

$ip = '192.168.1.5x';
$ips = ['192.168.1.3', '192.168.1.4', '192.168.1.5',];

if (!array_reduce($ips,function($value,$item)  use ($ip) {
    return $value || $item === $ip ? true : false;
},false)) {
    throw new Exception('Oopsie');
}
echo "Got it with no gotos\n";
share|improve this answer
    
Ewwwwwwwwwwwwww – immibis 3 hours ago

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.