Young programmers hear that goto is bad and should never be used. They begin repeating this to others. Then the whole world thinks goto is bad, because 99% of devs say so.
Then an expert, highly-regarded dev shows code that has useful goto statements. And only 1% of coders understand that this is perfectly acceptable. They know that goto is not evil (all the time) and is even very useful in certain cases.
Goto statements are so widely shunned that for a long time, I didn't even realize that C had one. It wasn't until I started reading some Linux code that I found out that it existed.
At first I was more than a little bit turned off by the practice, but once I understood _how_ it was being used, it made a lot of sense. The bottom line is, goto statements which jump backwards are generally Considered Harmful. Goto statements which jump forwards, within a limited scope, and used conservatively, can serve to improve the readability of code.
I still almost never use them, but now I know when it would be appropriate to do so, rather than dogmatically rejecting the entire practice. I think the idea that gotos are always bad is one of those things that programmers tend to accept as "received wisdom", without considering the actual motivation for the principle, or in which cases it might not apply as strongly.
The Linux kernel does not just use gotos to "jump forwards." If you look careful, they've implemented a simplified version of try-catch-finally. It's a structured use of gotos, implementing clear semantics.
I didn't mean to suggest that Linux kernel gotos were just arbitrary forward jumps. I know that they are used for error handling. That's the legitimate usage I was alluding to, I just wasn't specific about it.
Gotos were traditionally discouraged to students since they would write code that was needlessly hard to follow (and buggy as a result), so instructing them to not use gotos improved the readability of their code and improved their thought processes.
That being said, Richard Stevens, a respected author on Unix programming, had this to say:
Read Structured Programming with go to Statements by Knuth in the ACM Computing Surveys, Vol. 6, No. 4, Dec. 1974 issue. (In fact, this entire issue of Computing Surveys is a classic.) My challenge to the goto-less programmer is to recode tcp_input() (Chapters 27 and 28 of TCP/IPIv2) without any gotos ... without any loss of efficiency (there has to be a catch).
In C with it's lack of exceptions, goto can be used to jump unconditionally to cleanup code before exiting a function. This is far cleaner than every branch and loop following a call needing explicit instructions to check for error statuses, sometimes multiple errors (like malloc something, check for error, use dynamic memory in a call, check for a different error).
It's also more maintainable, since the error checking is done in a single place and a single jump. Without a goto, if there were a new error condition to check, it might take several updates to ensure all error conditions are accurately checked.
Generally, gotos for handling exceptional cases inside a function: okay. Goto for implementing twisted logic: bad.
I wish I had a link, but I recall reading that Dijkstra said the backlash against goto's was overblown. You hear the same kind of dogma repeated about globals, multiple points of return, and others. It's not that these constructs should never be used, it's that they should be used judiciously.
They don't really mention it in the style guideline but there are two primary reasons for using goto in C and they're to make up for two other language deficiencies.
The first is the lack of exceptions. This is not necessarily a deficiency in kernel code because it can make other code more complex, but fundamentally error handling and cleanup usually either requires 1) keeping track of a bunch of state and then using complicated logic to evaluate the state for error cases 2) exception catching such that error conditions can be thrown up, caught, and brought to a cohesive resolution or 3) goto, such that cohesive cleanup code can be placed together in a segment and then jumped to on error conditions. (3) is far superior to (1) and, while (2) is arguably superior to (3), it simply doesn't exist in C.
The second case is that C doesn't have a multi-level break. That is, when in multiple nested blocks (loops), in order to exit early one would have to keep track of state and use conditionals to trigger a "waterfall" break all the way down the block chain. goto solves this by allowing you to jump out of the chain completely. I think most people would probably agree that adding a multilevel break (e.g., "break 2;") would probably be preferable to using goto here, but I think the workaround is basic enough (either use goto or refactor into separate functions) that the language addition is unlikely now.
switch( machineState ){
case ALPHA:
...
break;
case BETA:
...
goto location;
case GAMMA:
location:
...
}
No, I wasn't familiar with Duff's device until just now. I'd say that you only need to be explicit about pointing out fallthroughs if the blocks in the switch statements are more than a couple of lines long.
In this case it might be clearer to just run the switch again after ALPHA and BETA with GAMMA as the machineState, and if necessary, have logic in GAMMA for switching machineState back to ALPHA or BETA.
It also might be clearer to move GAMMA outside the switch block entirely:
int shouldGamma = 0;
switch ( machineState ) {
case ALPHA:
... alpha prep ...
shouldGamma = 1;
break;
case BETA:
... beta prep ...
shouldGamma = 1;
break;
case GAMMA:
shouldGamma = 1;
break;
}
if(shouldGamma) {
// GAMMA stuff here
}
The shouldGamma flag would be unnecessary if all possible values for machineState should still run the code in GAMMA.
I think goto's are fine, and they are often more readable than large amounts of indentation. That's _especially_ true if the code flow isn't actually naturally indented (in this case it is, so I don't think using goto is in any way _clearer_ than not, but in general goto's can be quite good for readability).
Of course, in stupid languages like Pascal, where labels cannot be descriptive, goto's can be bad. But that's not the fault of the goto, that's the braindamage of the language designer.
If coding without goto statements makes code flow easier to understand, consider taking the next step and coding without if() statements!
Many conditional statements and sentinel null values can be replaced with strategy objects or mock objects, revealing a more functional programming style that can make testing easier.
21 comments
[ 3.5 ms ] story [ 57.5 ms ] threadThen an expert, highly-regarded dev shows code that has useful goto statements. And only 1% of coders understand that this is perfectly acceptable. They know that goto is not evil (all the time) and is even very useful in certain cases.
At that level it's all goto so you have to use them. And if you don't want your program to become a spaghetti nightmare you have to use them wisely.
When using anything higher level you can & should use 'if', 'for', 'while', etc. but it's not like the world implodes if you use a goto in your code.
At first I was more than a little bit turned off by the practice, but once I understood _how_ it was being used, it made a lot of sense. The bottom line is, goto statements which jump backwards are generally Considered Harmful. Goto statements which jump forwards, within a limited scope, and used conservatively, can serve to improve the readability of code.
I still almost never use them, but now I know when it would be appropriate to do so, rather than dogmatically rejecting the entire practice. I think the idea that gotos are always bad is one of those things that programmers tend to accept as "received wisdom", without considering the actual motivation for the principle, or in which cases it might not apply as strongly.
That being said, Richard Stevens, a respected author on Unix programming, had this to say:
http://www.kohala.com/start/rstevensfaq.html Q: Why do your programs contain gotos?
Read Structured Programming with go to Statements by Knuth in the ACM Computing Surveys, Vol. 6, No. 4, Dec. 1974 issue. (In fact, this entire issue of Computing Surveys is a classic.) My challenge to the goto-less programmer is to recode tcp_input() (Chapters 27 and 28 of TCP/IPIv2) without any gotos ... without any loss of efficiency (there has to be a catch).
It's also more maintainable, since the error checking is done in a single place and a single jump. Without a goto, if there were a new error condition to check, it might take several updates to ensure all error conditions are accurately checked.
Generally, gotos for handling exceptional cases inside a function: okay. Goto for implementing twisted logic: bad.
The first is the lack of exceptions. This is not necessarily a deficiency in kernel code because it can make other code more complex, but fundamentally error handling and cleanup usually either requires 1) keeping track of a bunch of state and then using complicated logic to evaluate the state for error cases 2) exception catching such that error conditions can be thrown up, caught, and brought to a cohesive resolution or 3) goto, such that cohesive cleanup code can be placed together in a segment and then jumped to on error conditions. (3) is far superior to (1) and, while (2) is arguably superior to (3), it simply doesn't exist in C.
The second case is that C doesn't have a multi-level break. That is, when in multiple nested blocks (loops), in order to exit early one would have to keep track of state and use conditionals to trigger a "waterfall" break all the way down the block chain. goto solves this by allowing you to jump out of the chain completely. I think most people would probably agree that adding a multilevel break (e.g., "break 2;") would probably be preferable to using goto here, but I think the workaround is basic enough (either use goto or refactor into separate functions) that the language addition is unlikely now.
It also might be clearer to move GAMMA outside the switch block entirely:
The shouldGamma flag would be unnecessary if all possible values for machineState should still run the code in GAMMA.I think goto's are fine, and they are often more readable than large amounts of indentation. That's _especially_ true if the code flow isn't actually naturally indented (in this case it is, so I don't think using goto is in any way _clearer_ than not, but in general goto's can be quite good for readability).
Of course, in stupid languages like Pascal, where labels cannot be descriptive, goto's can be bad. But that's not the fault of the goto, that's the braindamage of the language designer.
http://kerneltrap.org/node/553/2131
Many conditional statements and sentinel null values can be replaced with strategy objects or mock objects, revealing a more functional programming style that can make testing easier.
http://programmingwithoutifs.blogspot.com/