Go To Statement Considered Harmful
You probably shouldn’t use goto statements in your code.
The main reason you shouldn’t use them is because they make some people freak out. They’ve just heard that it’s harmful. That’s all they know. There’s a lot of things like that in life — maybe most things are like that: you don’t win any points by outsmarting the status quo.
“Go To Statement Considered Harmful” was a letter by Edgser Dijkstra to the ACM publication. It became so famous “[something] Considered Harmful” has become a strong meme in the computer science community.
In most languages, goto is not actually harmful. I’ll get to that in a moment. But before I establish goto isn’t that bad, I want to argue that it doesn’t matter. The point is to pick your battles. When you go against the grain by outsmarting common knowledge, most people don’t have the depth of knowledge to see that you’re right. Sometimes, some of the people will outright lack the cognitive capacity to ever understand.
The important thing in your career is to give people what they want and/or expect. For a lot of people who stay up to date on trends, what they want and expect is the topic of the latest Martin Fowler presentation. Other people want and expect some really ancient form of Best Practice like copious UML diagrams. The point is, people form opinions on what’s smart, and they fully expect that all the smart people do it that way, and all the people who do it that way are smart. If you end up outsmarting somebody’s personal definition of smart, they won’t understand that you’re upping the ante, they won’t understand your reasons for disagreeing. Persuasive arguments won’t work because your ethos won’t exceed that of whatever source they got their ideas from. No matter how well you can state your case, people will just think you don’t get it because you’re dumb, and you’re dumb because you don’t get it.
meme-based comic
Dijkstra wrote his original article in 1968. Those were very different times. Computer Scientists like Dijkstra were still endeavoring to establish the basic principles of programming and programming languages. All the computers in the world were literally known on an individual first name basis. Even simple programs had to be laboriously written, and great care was taken to try to prove the code was right before they wasted their time running the program. Dijkstra, then, was rejecting goto because he was advocating nascent program structures like subprocedures, functions, loops, etc., which provide higher level abstractions than the if-then-goto code pattern which had often been used. He saw the issue as dichotomous, but it was always a false dichotomy. He didn’t anticipate that structured programming and goto could live alongside one another, as long as goto could be restricted in certain key ways. Consider this excerpt:
The unbridled use of the go to statement has an immediate consequence that it becomes terribly hard to find a meaningful set of coordinates in which to describe the process progress. Usually, people take into account as well the values of some well chosen variables, but this is out of the question because it is relative to the progress that the meaning of these values is to be understood! With the go to statement one can, of course, still describe the progress uniquely by a counter counting the number of actions performed since program start (viz. a kind of normalized clock). The difficulty is that such a coordinate, although unique, is utterly unhelpful. In such a coordinate system it becomes an extremely complicated affair to define all those points of progress where, say, n equals the number of persons in the room minus one!
— Edgser Dijkstra
Dijkstra’s argument that goto was incompatible with structured programming hinges on this idea about goto screwing up “coordinates.” What the heck are coordinates? It may appear he’s talking about loop counters or something. But that doesn’t actually make sense. Allow me to translate: In modern terms, we have a coordinate system we call the callstack. What Dijkstra was saying was that the goto statement will mess up the callstack. He’s essentially talking about goto being used to jump into or out of a function without resetting a stack frame. Scoped variables would not be cleaned up and the proper ones wouldn’t be allocated. But actually in modern compilers and languages, this is impossible — the compiler scopes the goto labels just like it scopes variables, and does flow analysis to verifiy, e.g., goto isn’t used to skip a variable declaration. This is why it was a false dichotomy — we have ways to statically verify that goto isn’t used in the ways that would wreck the structure of the program.
Less known than Dijkstra’s “considered harmful” letter, less-ornery computer scientist Donald Knuth answered with a paper about structured programming including examples where goto is one of the most effective ways to achieve the desired result. Knuth shows, on the contrary, goto and structured programming can live together in harmony. And, as mentioned, modern compilers can prevent goto being used to screw up the “coordinate” system.
But a lot of people know the “Go To Statement Considered Harmful” meme, and few know Knuth’s retort. Even fewer understand either one. When asked about why goto is bad, most people will say it makes the program flow hard to read. (Most also think that’s what Dijkstra’s article says, even after they read it!) That’s nonsense. The typical, pre-1968 goto code flow would look something like this:
if ($WAITING) then goto INPUT
In 2017, the same code would look like this:
if (isWaiting){GetUserInput();}
Either way, the code flow is going to be exactly the same! On the other hand, when it comes to most of the modern day valid uses of goto (read: scoped to within a single function,) people are still doing “goto” programming, they’re just doing it with more verbose and costly boolean flags and if statements. Any goto code re-written with if statements will always be substantially less readable and sometimes marginally less performant.
Consider this code, a reasonable use of the goto statement, which breaks nested loops and skips some code if a condition is met.
/* find a marker in a matrix if one exists or place a default */int row = 0;int col = 0;while (row < height) {while (col < width) {if (matrix[row][col] == MARKER_VAL) {goto FOUND;}col++;}row++;}
NOT_FOUND://place marker in approx. middle as defaultrow = height / 2;col = width / 2;
FOUND:compute_distances(matrix, row, col);
Depending on what language you’re using, there are many alternative ways to write this code. None of them will be any more readable, nor any more safe. Using if
statements, in particular, would be substantially less readable.
The basic underlying context of Dijkstra’s original paper — structured programming — is already successfully woven into all of our well-structured, 3rd generation programming languages. But within that framework, there’s nothing wrong with goto, in most of the languages which properly scope it. Check your language manual. If gotos are compiled directly into JUMP or BRANCH instructions, you might have a problem. In most languages, that’s not the case. C++, C#, and Java’s compilers will all correctly refuse to compile if you do something unsafe with a goto statement, like skip a variable declaration, or attempt to jump to a label inside a different function. The only problems you can cause with goto in these languages are 100% held in common with any control flow structure such as if
. The main exception here is C, which is a few decades older. If you’re using plain C on an embedded system, there may be neither compilers nor other tools to help you statically verify the safety of your gotos, so keep your distance.
The fact is, goto is the victim of an irrational taboo. So badly constricted is the public discourse around goto, it can never be discussed without the speaker compulsively blurting out how bad it is. The modern programming construct which causes the most unreadable spaghetti code is object-oriented inheritance, which is readily misused, becomes unreadable an order of magnitude faster than goto, and has its own unique classification of anti-patterns which can lead to bugs. But no such taboo exists for inheritance, instead we simply point out that misusing it in those ways is bad programming, the fault of the programmer not of the construct itself. Why then, doesn’t the same logic apply to goto? Because it’s not rational to begin with; it’s a taboo. Goto is fine.
But don’t tell people that. They’ll think you’re dumb.
As I predicted in the first half of the article, many readers have a strong knee-jerk reaction and refuse to even entertain, even if temporarily and hypothetically, any alternative beliefs about goto. There’s not much I can do about this, because it’s as I predicted — the taboo is strong, and most people are mentally incapable of breaking their minds out of it. But there is one issue I can answer. I had a rather rude reader insisting I am wrong or BSing that Dijkstra’s famous article is about goto
messing up the callstack.
It absolutely is. I should have linked Dijkstra’s essay originally. Here is a quote, near the beginning, in which he clearly establishes that what we know as the callstack is the central premise of his paper. I realize his tone is hard to read, but if you concentrate you’ll get it. The original is in the link, but I’ll shorten the excerpt here with ellipses to abbreviate his very repetitive, overly-defensive, academic writing style. Emphasis mine:
Let us now consider how we can characterize the progress of a process. […] If the program text is a pure concatenation of, say, assignment statements […] it is sufficient to point in the program text to a point between two successive action descriptions. […] Let us call such a pointer to a suitable place in the text a “textual index.”
[…]
As soon as we include in our language procedures we must admit that a single textual index is no longer sufficient. In the case that a textual index points to the interior of a procedure body the dynamic progress is only characterized when we also give to which call of the procedure we refer. With the inclusion of procedures we can characterize the progress of the process via a sequence of textual indices, the length of this sequence being equal to the dynamic depth of procedure calling.
Depth of the procedure calling? It’s the callstack. The callstack is literally exactly what “Go To Statement Considered Harmful” is all about. It’s impossible to understand Dijkstra’s essay, and therefore impossible to understand his argument against goto
without understanding that much. And, as I argued in my article above, Dijkstra’s argument is obsolete because goto
in modern languages is distinctively limited to within a single layer of the callstack.