When we're taught programming (at least me, anyway), we're always told that goto statements are to be avoided at all costs.
If they make code so difficult to read when used, why are they still in programming languages, and when are they useful?
When we're taught programming (at least me, anyway), we're always told that goto statements are to be avoided at all costs.
If they make code so difficult to read when used, why are they still in programming languages, and when are they useful?
At a low level they are sometimes useful I guess. I remember I used them in the previous century when programming in BASIC. With todays modern languages, there is absolutely no need you should use them, unless you like to write "spagetti" code with all the not needed problems that can come with it.
It is not, because a language has a feature that you should use it.
So, please don't use goto.
It dates back to 1968 when Dijstraka published his famous "Goto considered harmful" letter in the Communications of the ACM.
Until then goto was considered a normal essential part of any language. I was working in IBM's software products division in 1969, and headed a project to re-write our coding standards in light of the almost 100% acclaim his letter produced, and we never used goto's again.
Loops have an implicit goto when the terminating condition is met, but it is a compiler construct. Goto's have often been used in lieu of exceptions to break out of a bit of code when an exceptional (bad) situation has occured. These days, most modern languages have exception handling, so instead you would throw the appropriate exception. It is a goto in effect. I think it is still "lipstick on a pig"... :-)
Besides a few crazy little tricks that rely on goto's, there are two canonical use-cases for goto's. The goto's are not absolutely necessary in those cases (you could do without them), but they are not too harmful and make things somewhat simpler. That said, the gag reflex of most programmers to seeing a goto, or worse, writing one, is very well justified in general.
CASE 1
The first canonical case is the "break out of multiple loops" problem. This boils down to wanting to do a break
that drops you out of more than one nested loop. Like this:
void foo() {
// ...
for(int i = 0; i < N; ++i) {
for(int j = 0; j < M; ++j) {
//..
if ( something was found )
goto end_of_loops;
};
};
end_of_loops:
// ...
};
This is not too bad, because it's pretty safe and easy to understand (doesn't make "spaghetti code"). And the alternatives are not particularly nice.
One alternative is to use a flag to relay the break to the outer loop:
void foo() {
// ...
for(int i = 0; i < N; ++i) {
bool should_break = false;
for(int j = 0; j < M; ++j) {
//..
if ( something was found ) {
should_break = true;
break;
};
};
if( should_break )
break;
};
// ...
};
Which is a lot of additional code that is just "noise", and just to avoid the infamous "goto".
Another alternative is to wrap the loops in a function which can be broken out of by a simple return statement, like so:
int bar(/* .. */) {
for(int i = 0; i < N; ++i) {
for(int j = 0; j < M; ++j) {
//..
if ( something was found )
return (what was found);
};
};
return (some invalid value); // or: throw something_no_found();
};
void foo() {
// ...
int find_res = bar(/*..*/);
// ...
};
Which might be difficult to do in practice, like having to pass a lot of information back and forth to that helper function. And also by pulling out the code of the loops from its context, it can hurt the clarity of the code (and thus, creating "spaghetti code").
There are other alternatives for specific situations (and throwing an exception that is caught outside the outer loop is one too, which is not really different from using a goto), but the point is that in this case, the solution that uses goto is pretty much the simplest and clearest. And it is not that unusual to see this use of goto in real code.
CASE 2
The second canonical case is the "goto cleanup" pattern. This is often replaced by exceptions in modern code, but there are many contexts in which exceptions are not used (for other, more complicated reasons) or the code is in a language without exceptions, most notably, C. The idea is pretty simple, you put all your code to clean things up (deallocate memory, free resources, close files, etc..) at the end of the function, under a "cleanup" label, and you goto
to it instead of doing a return
statement. Like this:
int foo() {
int result = (all is OK);
// ... lots of allocation of resources ..
if ( some error detected ) {
result = (some error code);
goto cleanup;
};
// .. more code, with more "return points"..
cleanup:
// code to free up the resources allocated at the start.
return result;
};
The point is that without the goto statement, you would have to repeat the clean-up code (freeing resources) at every point where the function returns, which is horrible for readability and it is very error-prone (and if you forget, you will leak resources).
In C, or other languages without support for exceptions, there is pretty much no alternative to this pattern, and therefore, it is a very common thing to see. Similarly, you can see this from time to time in C++ when people avoid exceptions by choice, like I said earlier.
In C++, or other languages with good exception support, the alternative is to wrap each resource in a RAII class (which they usually are anyways, becuase this principle is ubiquitous in C++), and you would return from the function by either throwing an exception or with a return
statement. Either way, all the RAII objects holding the resources are automatically destroyed as the function is exited (by exception or return), therefore, gracefully and deterministically releasing all the resources. As so:
// The "error-code" version:
int foo() {
// ... lots of allocation of resources in RAII objects ..
if ( some error detected )
return (some error code);
// .. more code, with more "return points"..
return (all is OK, made it to the end);
}; // resources freed automatically at the exit.
// The "exception" version:
void foo() {
// ... lots of allocation of resources in RAII objects ..
if ( some error detected )
throw (some exception);
// .. more code, with more "return points"..
}; // resources freed automatically at the exit or stack unwinding.
In languages with support for exceptions, but where RAII is not possible, like Java or C#, then there isn't much of an alternative to the "goto cleanup" code, except for turning it into the equally ugly try-finally pair:
void CFoo::foo() {
// ... declare handles for resources ..
try {
// ... allocation of resources ..
if ( some error detected )
throw (some exception);
// .. more code, with more "return points"..
} catch( Exception e ) {
throw e;
} finally {
// code to manually free up the resources allocated at the start.
};
};
This probably what rubberman refers to by "lipstick on a pig". ;)
Languages like Java and C# have a "finally" block in their try-catch blocks specifically for this reason and because they lack support for automatic deterministic destruction of objects (needed for RAII). But finally blocks are nothing more than a goto cleanup;
in disguise, i.e., lipstick on a pig.
That pretty much sums up the use-cases for goto. Except those two cases (which have limited applicability or recurrence), I have never really seen any other uses of goto that are both justifiable and commonly seen in real code.
throw the appropriate exception. It is a goto in effect
By that token, everything is a goto in effect. And in reality, of course, everything is just a goto, because those (jump) operations are the bread-and-butter of machine code (if-statements, loops, switches, breaks / continues, exceptions, function calls, etc..., are all just goto's in disguise). The point is the structure of the code (or lack thereof) that "raw" goto's create that is harmful. I don't think that exceptions are nearly as bad as goto's, especially not in languages that can use RAII to clean things up automatically (but using exceptions can lead to exceedingly ugly code in languages like Java or C#).
Beautifully argued and presented, but as a Java progammer I have to disagree..
CASE 1:
loop1: for ....
...
loop2: while ...
...
break loop1;
...
}
}
How much clearer or safer do you want it?
CASE 2:
I don't understand you dislike of finally
. It provides an intrinsic, safe, simple and clear way to guarantee cleanup. It also opened the door for the subsequent try-with-resources, that makes cleanup even simpler and safer. Of course you can achieve the same thing by writing spurious code, or adopting a fragile and unverifiable convention of goto cleanup
s, but why?
One man's "ugly" is another man's "simple and safe". There's no accounting for taste.
... just putting my case... I don't want to start a flame war. J
ps: My previous mention of the 1969/70 IBM standards was a bit too brief. Many of the languages at the time had no support for code blocks in ifs or loops, so gotos were unavoidable. What we did do is to prove that all the necessary structures could be simulated using forward gotos, and mandated those where necessary. I still agree that forward gotos are not necessarily evil; the whole spaghetti code nightmare only comes if you allow backward gotos (which we didn't).
We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.