Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

My boss keeps mentioning nonchalantly that bad programmers use break and continue in loops.

I use them all the time because they make sense; let me show you the inspiration:

function verify(object) {
    if (object->value < 0) return false;
    if (object->value > object->max_value) return false;
    if (object->name == "") return false;
    ...
}

The point here is that first the function checks that the conditions are correct, then executes the actual functionality. IMO same applies with loops:

while (primary_condition) {
    if (loop_count > 1000) break;
    if (time_exect > 3600) break;
    if (this->data == "undefined") continue;
    if (this->skip == true) continue;
    ...
}

I think this makes it easier to read & debug; but I also don't see a downside. Please comment.

share|improve this question
2  
Doesn't take much to forget which one does what. –  user1249 Mar 15 '11 at 16:02
27  
No. Neither is goto. Knowing when to use them is the key. They are tools in the toolbox. You use them when they provide clear & succinct code. –  orj Mar 15 '11 at 20:41
33  
I cannot voice my support for this style of coding strongly enough. Multiple levels of nested conditionals are so much worse than this approach. I'm usually not militant about coding style, but this is almost a deal-breaker for me. –  Emil H Mar 16 '11 at 5:39
4  
Obviously your boss doesn't write (enough) code. If he did, he would know that all keywords (yes, even goto) are useful in some cases. –  faif Mar 26 '11 at 9:39
15  
Bad programmers use break and continue doesn't mean that good programmers don't. Bad programmers use if and while as well. –  mouviciel Nov 27 '13 at 14:22

19 Answers 19

up vote 102 down vote accepted

When used at the start of a block, as first checks made, they act like preconditions, so it's good.

When used in the middle of the block, with some code around, they act like hidden traps, so it's bad.

share|improve this answer
36  
+1 for breaking down the uses. ... get it? breaking? –  Mikhail Mar 15 '11 at 15:16
35  
... just don't continue ;) –  Klaim Mar 15 '11 at 15:18
3  
@bit-twiddler: I agree on the principle, but the purpose of a routine isn't directly related to it's behaviour. Doing one thing might result in different results, that might, or not, have been generated by different instruction flows. As far as it is correct and, indeed, does only one thing, and does it well, then the way it's written inside is a totally orthogonal problem. I believe that's why we like to enforce abstraction through encapsulation (hidding the guts of the process), polymorphism (hidding wich implemenation will be called), generic programming and other abstraction techniques. –  Klaim Mar 16 '11 at 13:55
26  
@bit-twiddler: this is a very C-ish mindset. A temporary variable can be modified later on, so a single typo 20 lines down from here could erase that carefully crafted result. An immediate return (or break, or continue) however is extremely clear: I can stop reading now because I know it can't possibly be modified further down. It's good for my little brain, really makes trudging through the code easier. –  Matthieu M. Mar 17 '11 at 19:00
7  
@Matthieu I agree. Exit a block when you receive a result that satisfies the block's purpose. –  Evan Plaice Mar 19 '11 at 5:48

Yes you can [re]write programs without break statements (or returns from the middle of loops, which do the same thing). But you may have to introduce additional variables and/or code duplication both of which typically make the program harder to understand. Pascal (the programming language) was very bad especially for beginner programmers for that reason. Your boss basically wants you to program in Pascal's control structures. If Linus Torvalds were in your shoes, he would probably show your boss the middle finger!

There's a computer science result called the Kosaraju's hierarchy of control structures, which dates back to 1973 and which is mentioned in Knuth's (more) famous paper on gotos from 1974. (This paper of Knuth was already recommended above by David Thornley, by the way.) What S. Rao Kosaraju proved in 1973 is that it's not possible to rewrite all programs that have multi-level breaks of depth n into programs with break depth less than n without introducing extra variables. But let's say that's just a purely theoretical result. (Just add a few extra variables?! Surely you can do that to please your boss...)

What's far more important from a software engineering perspective is a more recent, 1995 paper by Eric S. Roberts titled Loop Exits and Structured Programming: Reopening the Debate (http://cs.stanford.edu/people/eroberts/papers/SIGCSE-1995/LoopExits.pdf). Roberts summarizes several empirical studies conducted by others before him. For example, when a group of CS101-type students were asked to write code for a function implementing a sequential search in an array, the author of the study said the following about those students who used a break/return/goto to exit the from the sequential search loop when the element was found:

I have yet to find a single person who attempted a program using [this style] who produced an incorrect solution.

Roberts also says that:

Students who attempted to solve the problem without using an explicit return from the for loop fared much less well: only seven of the 42 students attempting this strategy managed to generate correct solutions. That figure represents a success rate of less than 20%.

Yes, you may be more experienced than CS101 students, but without using the break statement (or equivalently return/goto from the middle of loops), eventually you'll write code that while nominally being nicely structured is hairy enough in terms of extra logic variables and code duplication that someone, probably yourself, will put logic bugs in it while trying to follow your boss' coding style.

I'm also gonna say here that Roberts' paper is far more accessible to the average programmer, so a better first read than Knuth's. It's also shorter and covers a narrower topic. You could probably even recommend it to your boss, even if he is the management rather than the CS type.

share|improve this answer

Absolutely not... Yes the use of goto is bad because it deteriorates the structure of your program and also it is very difficult to understand the control flow.

But use of statements like break and continue are absolutely necessary these days and not considered as bad programming practice at all.

And also not that difficult to understand the control flow in use of break and continue. In constructs like switch the break statement is absolutely necessary.

share|improve this answer
1  
I haven't used "continue" since I first learned C in 1981. It is an unnecessary language feature, as the code that is bypassed by the continue statement can be wrapped by a conditional control statement. –  bit-twiddler Mar 15 '11 at 15:24
6  
I prefer to use continue in those cases since it makes sure that my code doesn't become arrow code. I hate arrow code more than goto type statements. I also read it as, "if this statement is true, skip the rest of this loop and continue with the next iteration." Very useful when it's at the very beginning of a for loop (less useful in while loops). –  jsternberg Mar 15 '11 at 15:48

Bad programmers speak in absolutes (just like Sith). Good programmers use the clearest solution possible (all other things being equal).

Using break and continue frequently makes code hard to follow. But if replacing them makes the code even harder to follow, then that's a bad change.

The example you gave is definitely a situation where the breaks and continues should be replaced with something more elegant.

share|improve this answer

As long as they're not used as disguised goto like in the following example :

do
{
      if (foo)
      {
             /*** code ***/
             break;
      }

      if (bar)
      {
             /*** code ***/
             break;
      }
} while (0);

I'm fine with them. (Example seen in production code, meh)

share|improve this answer

@umlcat:

Your code

String myKey = "mars";

for (i = 0; i < MyList.Count; i++) {
  if (MyList[i].key == myKey)
    break;
}
ShowMessage("Key is " + i.toString());

if (i < MyList.Count)  // I corrected this omission for you
    ShowMessage("Index is " + i.toString());
else
    ShowMessage("Not found.");

can be replace by

String myKey = "mars";

for (i = 0; i < MyList.Count && MyList[i].key != myKey; i++) 
    ;

if (i < MyList.Count)
    ShowMessage("Index is " + i.toString());
else
    ShowMessage("Not found.");
share|improve this answer

Code Complete has a nice section about using goto and multiple returns from routine or loop.

In general it's not bad practice. break or continue tell exactly what happens next. And I agree with this.

Steve McConnell (author of Code Complete) uses almost the same examples as you to show advantages of using various goto statements.

However overuse break or continue could lead to complex and unmaintainable software.

share|improve this answer

The example you gave doesn't need breaks nor continues:

while (primary-condition AND
       loop-count <= 1000 AND
       time-exec <= 3600) {
   when (data != "undefined" AND
           NOT skip)
      do-something-useful;
   }

My ‘problem’ with the 4 lines in your example is that they are all on the same level but they do different things: some break, some continue... You have to read each line.

In my nested approach, the more deeper you go, the more ‘useful‘ the code becomes.

But, if deep inside you'd find a reason to stop the loop (other than primary-condition), a break or return would have it's use. I'd prefer that over the use of an extra flag that is to be tested in the top-level condition. The break/return is more direct; it better states the intent than setting yet another variable.

share|improve this answer

I would replace your second code snippet with

while (primary_condition && (loop_count <= 1000 && time_exect <= 3600)) {
    if (this->data != "undefined" && this->skip != true) {
        ..
    }
}

not for any reasons of terseness - I actually think this is easier to read and for someone to understand what is going on. Generally speaking the conditions for your loops should be contained purely within those loop conditions not littered throughout the body. However there are some situations where break and continue can help readability. break moreso than continue I might add :D

share|improve this answer

No. It's a way to solve a problem, and there are other ways to solve it.

Many current mainstream languages (Java, .NET (C# + VB), PHP, write your own) use "break" and "continue" to skip loops. They both "structured goto (s)" sentences.

Without them:

String myKey = "mars";

int i = 0; bool found = false;
while ((i < MyList.Count) && (not found)) {
  found = (MyList[i].key == myKey);
  i++;   
}
if (found)
  ShowMessage("Key is " + i.toString());
else
  ShowMessage("Not found.");

With them:

String myKey = "mars";

for (i = 0; i < MyList.Count; i++) {
  if (MyList[i].key == myKey)
    break;
}
ShowMessage("Key is " + i.toString());

Note that, "break" and "continue" code is shorter, and usually turns "while" sentences into "for" or "foreach" sentences.

Both cases are a matter of coding style. I prefer not to used them, because the verbose style allows me to have more control of the code.

I actually, work in some projects, where it was mandatory to use those sentences.

Some developers may think they are not necesarilly, but hypothetical, if we had to remove them, we have to remove "while" and "do while" ("repeat until", you pascal guys) also ;-)

Conclusion, even if I prefer not to use them, I think its an option, not a bad programming practice.

share|improve this answer
2  
not to mention that the first example only works if the key is the last one in the list. –  Mikhail Mar 15 '11 at 18:41

I disagree with your boss. There are proper places for break and continue to be used. In fact the reason that execeptions and exception handling were introduced to modern programming languages is that you can't solve every problem using just structured techniques.

On a side note I don't want to start a religious discussion here but you could restructure your code to be even more readable like this:

while (primary_condition) {
    if (loop_count > 1000) || (time_exect > 3600) {
        break;
    } else if ( ( this->data != "undefined") && ( !this->skip ) ) {
       ... // where the real work of the loop happens
    }
}

On another side note

I personally dislike the use of ( flag == true ) in conditionals because if the variable is a boolean already, then you are introducing an additional comparison that needs to happen when the value of the boolean has the answer you want - unless of course you are certain that your compiler will optimize that extra comparison away.

share|improve this answer
1  
@Pete - The term is not terse but elegant. You can waffle all you want, but if you are worried that the reader/maintainer doesn't understand what a boolean is or what the terse/elegant terminology means is then maybe you better hire some smarter maintainers ;-) –  Zeke Hansell Mar 15 '11 at 19:30

The essential notion comes from being able to semantically analyze your program. If you have a single entry and a single exit, the math needed to denote possible states is considerably easier than if you have to manage forking paths.

In part, this difficulty reflects out into being able to conceptually reason about your code.

Frankly, your second code is not obvious. What is it doing? Does continue 'continue', or does it 'next' the loop? I have no idea. At least your first example is clear.

share|improve this answer

I don't like either of these styles. Here's what I would prefer:

function verify(object)
{
    if not (object->value < 0) 
       and not(object->value > object->max_value)
       and not(object->name == "") 
       {
         do somethign important
       }
    else return false; //probably not necessary since this function doesn't even seem to be defined to return anything...?
}

I really don't like using return to abort a function. It feels like an abuse of return.

Using break also is not always clear to read.

Better yet might be:

notdone := primarycondition    
while (notDone)
{
    if (loop_count > 1000) or (time_exect > 3600)
    {
       notDone := false; 
    }
    else
    { 
        skipCurrentIteration := (this->data == "undefined") or (this->skip == true) 

        if not skipCurrentIteration
        {
           do something
        } 
        ...
    }
}

less nesting and the complex conditions are refactored into variables (in a real program you'd have to have better names, obviously...)

(All above code is pseudo-code)

share|improve this answer
8  
You would really prefer 3 levels of nesting over what I typed above? –  Mikhail Mar 15 '11 at 15:02
3  
Too much nesting destroys readability. And sometimes, avoiding the break/continue introduces the necessity of inverting your logic on your conditional tests, which can lead to misinterpretation of what your code is doing -- I'm just sayin' –  Zeke Hansell Mar 15 '11 at 18:45

You could read Donald Knuth's 1974 paper Structured Programming with go to Statements, in which he discusses various uses of the go to that are structurally desirable. They include the equivalent of break and continue statements (many of the uses of go to in there have been developed into more limited constructs). Is your boss the type to call Knuth a bad programmer?

(The examples given interest me. Typically, break and continue are disliked by people who like one entry and one exit from any piece of code, and that sort of person also frowns on multiple return statements.)

share|improve this answer
4  
Most of the people who like functions and procedures to have single entry and exit points grew up on Pascal. Pascal was not the first language that I learned, but it had a profound impact on how I structure code to this day. People always comment on how easy it is to read my Java code. That's because I avoid multiple exits points as well as intermixing declarations with code. I try my best to declare every local variable used in a method at the top of the method. This practice avoids code ramble by forcing me to keep methods succinct. –  bit-twiddler Mar 15 '11 at 15:37
1  
Pascal also had nested functions. Just sayin'... –  Shog9 Mar 15 '11 at 17:32
4  
From what I remember, back in the day, the main reason that people didn't like multiple return statements in functions was because debuggers didn't handle them properly. It was a real pain to set a breakpoint at the end of a function but you never hit it because of an earlier return statement. For every compiler I use nowadays that is no longer an issue. –  Dunk Mar 15 '11 at 19:16
14  
@bit-twiddler: I'm not so sure about that. I'm still using Pascal today, and I generally regard "single entry single exit," or at least the single-exit part of it, as cargo cult programming. I just consider Break, Continue and Exit as tools in my toolbox; I use them where it makes the code easier to follow, and don't use them where it would make things harder to read. –  Mason Wheeler Mar 15 '11 at 21:32
1  
@bit-twiddler: I'll agree with the "short" methods: it should fit on my screen entirely, and I want to have two editors side by side so long lines are out of the equation too. However I strongly disagree with the one-entry one-exit mindset. This was done in early languages because of the necessity to clean-up manually (and thus avoided duplicating the clean-up) but with modern languages, clean-up is handled with other mechanisms (C# and Python using, Go defer, C++ RAII) which obsoletes this reason, and thus the rule. –  Matthieu M. Mar 17 '11 at 19:07

I do not believe they are bad. The idea that they are bad comes from the days of structured programming. It is related to the notion that a function must have a single entry point and a single exit point, i. e. only one return per function.

This makes some sense if your function is long, and if you have multiple nested loops. However, your functions should be short, and you should wrap loops and their bodies into short functions of their own. Generally, forcing a function to have a single exit point can result in very convoluted logic.

If your function is very short, if you have a single loop, or at worst two nested loops, and if the loop body is very short, then it is very clear what a break or a continue does. It is also clear what multiple return statements do.

These issues are addressed in "Clean Code" by Robert C. Martin and in "Refactoring" by Martin Fowler.

share|improve this answer
4  
+1 for historical reference (single point of exit). Same schooling also teaches not to write functions that are hard to read (cyclomatic complexity > 10) –  Mikhail Mar 15 '11 at 15:09
3  
"Make your functions small. Then make them smaller" -Robert C. Martin. I found that this works surprisingly well. Every time you see a block of code in a function that needs a comment explaining what it does, wrap it into a separate function with a descriptive name. Even if it is only a few lines, and even if it is only used once. This practice eliminates most of the issues with break/continue or multiple returns. –  Dima Mar 15 '11 at 15:13
1  
@Mikhail: Cyclomatic complexity is generally pretty strongly correlated with SLOC, which means that the advice can be simplified to "don't write long functions". –  John R. Strohm Mar 19 '11 at 16:54

The "badness" is dependent on how you use them. I typically use breaks in looping constructs ONLY when it will save me cycles that can't be saved through a refactoring of an algorithm. For instance, cycling through a collection looking for an item with a value in a specific property set to true. If all you need to know is that one of the items had this property set to true, once you achieve that result, a break is good to terminate the loop appropriately.

If using a break won't make the code specifically easier to read, shorter to run or save cycles in processing in a significant manner, then it's best not to use them. I tend to code to the "lowest common denominator" when possible to make sure that anyone who follows me can easily look at my code and figure out what's going on (I am not always successful at this). Breaks reduce that because they do introduce odd entry/exit points. Misused they can behave very much like an out of whack "goto" statement.

share|improve this answer

Most people think it's a bad idea because the behaviour isn't easily predictable. If you're reading through the code and you see while(x < 1000){} you assume it's going to run until x >= 1000...But if there are breaks in the middle, then that doesn't hold true, so you can't really trust your looping...

It's the same reason people don't like GOTO: sure, it can be used well, but it can also lead to godawful spaghetti code, where the code leaps randomly from section to section.

For myself, if I was going to do a loop that broke on more than one condition, I'd do while(x){} then toggle X to false when I needed to break out. The final result would be the same, and anyone reading through the code would know to look more closely at things that switched the value of X.

share|improve this answer
1  
+1; Let's say I count errors in a loop, so I don't expect to reach 1000 errors, but if I do - I want to break. –  Mikhail Mar 15 '11 at 15:04
3  
Mikhail: The issue with break is that the final condition for the loop is never simply stated in one place. That makes it difficult to predict the post-condition after the loop. In this trivial case (>= 1000) it's not hard. Add many if-statements and different levels of nesting it it can become very, very difficult to determine the post-condition of the loop. –  S.Lott Mar 15 '11 at 15:46
3  
Replacing break; with x=false; does not make your code more clear. You still have to search the body for that statement. And in the case of x=false; you'll have to check that it doesn't hit a x=true; further down. –  Sjoerd Mar 17 '11 at 14:57
4  
When people say "I see x and I assume y, but if you do z that assumption doesn't hold" I tend to think "so don't make that stupid assumption". Many people would simplify that to "when I see while (x < 1000) I assume it will run 1000 times". Well, there's many reasons why that's false, even if x is initially zero. For example, who says x is incremented precisely once during the loop, and never modified in any other way? Even for your own assumption, just because something sets x >= 1000 doesn't mean the loop will end - it may be set back in range before the condition gets checked. –  Steve314 Oct 6 '11 at 9:57

I don't consider using either of these bad practice, but using them too much within the same loop should warrant rethinking the logic being used in the loop. Use them sparingly.

share|improve this answer

I think it's only a problem when nested deeply inside multiple loops. It's hard to know which loop you are breaking to. It might be difficult to follow a continue also, but I think the real pain comes from breaks - the logic can be difficult to follow.

share|improve this answer
2  
Actually, it's easy to see if you indent properly, have the semantics of those statements internalized and aren't too sleepy. –  delnan Mar 15 '11 at 14:56
2  
Yeah, especially the last. –  Michael K Mar 15 '11 at 14:57

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.