share
Stack OverflowBreak statements In the real world
[+69] [44] Lodle
[2008-10-19 12:22:03]
[ coding-style break ]
[ http://stackoverflow.com/questions/216359/break-statements-in-the-real-world ] [DELETED]

Been having a discussion on whirlpool about using break statements in for loops. I have been taught and also read elsewhere that break statements should only be used with switch statements and with while loops on rare occasions.

My understanding is that you should only use for loops when you know the number of times that you want to loop, for example do work on x elements in an array, and while loops should be used every other time. Thus a for loop with a break can be easily refactored into a while loop with a condition.

At my university, you will instantly fail an assignment if you use break anywhere but in a switch statement as it breaks the coding guideline of the university. As I'm still completing my software engineering degree I would like to know from people in the real world.

Out of curiosity, is there anything in your school's guidelines about using <code>continue</code> in loops? - mmacaulay
a loop with continue is analogous to a function with early return (e.g. factor the loop body into a method). early return typically helps linear execution and statement locality, two readability metrics defined in Code Complete. Break and continue are very different. - Dustin Getz
Oh, I'm not against using continue, or break for that matter. I'm just wondering. - mmacaulay
We are allowed to use continue and i do so on a regular basis - Lodle
(13) it would depend how you use it. In my opinion those who say " never use" are wrong. Even goto statement has its uses. - Anycorn
"best-practice" has to be renamed into "banned-practice"! - AraK
(1) IMHO break, goto, continue etc. is like salt and pepper: Great if used in moderation. - Daniel Rikowski
(3) @DR Well not goto. You're kind of crossing the line there. Goto is more like horseradish sauce - hardly at all if any. - bobobobo
This looks like a duplicate of: stackoverflow.com/questions/2657104/…? - Vivin Paliath
(1) @Vivin Paliath That one is a duplicate of this one (which is a year and half older). - Tom Hawtin - tackline
@Tom Hawtin oops my bad. Didn't look at the date! - Vivin Paliath
@tzenes My hand is up - Taylor Satula
(2) I find it hard to believe there's a real university which enforces uniform coding standards on all courses. - shoosh
(1) "At my university, you will instantly fail an assignment if you use break anywhere but in a switch statement as it breaks the coding guideline of the university." <-- :( - Grant Paul
(4) Unfortunately, all too often the university isn't anything like the real world. - Loren Pechtel
@Loren: However, coding standards are not necessarily good. For example, Google's C++ coding standards are suited for Google and its code base, and shouldn't be used as widely as they've gotten. Having to adhere to suboptimal coding standards is sometimes like the real world. - David Thornley
(2) your uni lecturers sound like academic muppets. I recall my uni lecturers. None of them could code for shit. Those who can, do, those who can't - teach! - user206705
"a loop with continue is analogous to a function with early return (e.g. factor the loop body into a method). " -- And a loop with break is analogous to a function with an early return inside a loop. "Break and continue are very different." -- Wrong and contrary to control flow theory. - Jim Balter
[+175] [2008-10-19 12:42:52] paxdiablo

These generalized rules are rubbish as far as I'm concerned. Use what the language allows in the real world as long as it aids (or doesn't degrade) readability. The guideline against using break is no different to that against using goto. The reason behind people not liking them is because it may lead to spaghetti code, hard to follow.

Note the use of two phrases in that sentence above: The first was "guideline" instead of rule - the only rules are those imposed by the standards. Guidelines are for best practices but you have to understand the reasons behind them, not just follow them blindly.

The second was "may lead to" rather than "does lead to". There are situations where break and its brethren actually lead to more readable code than the alternative (which is often a hugely ugly condition in the looping statement).

For example, they make a lot of sense in finite state machines.

As some have pointed out, break can lead to post-conditions of a loop being variable. By that, I mean that:

for (i = 0; i < 50; i++) {
    if (someCondition) {
        break;
    }
}

can lead to i holding an indeterminate value after the loop.

But you should keep in mind that only matter if you actually care what i is set to after the loop. If the next statement is:

for (i = 0; i < 50; i++) { ... }

then it doesn't matter at all.

A piece of code like:

while (x != 0) {
    y = doSomethingWith (x);
    if (y == 0) break;
    process (y);

    z = doSomethingElseWith (x);
    if (z == 0) break;
    process (z);

    x--;
}

violates this guideline and can be refactored into something that doesn't but there is nothing unreadable about this piece of code. You can clearly see all flows of control at a single glance.

You should use the language features that make sense to your situation. Where guidelines should be enforced and where they should be ignored comes with experience.


(27) Bravo! Dogmatic adherence to strict guidelines sometimes does preclude the most elegant solutions. "Rules of Thumb" are not "Laws" for a reason. - dicroce
i agree with your premise ("do what increases readability"). i disagree with your conclusion ("breaks increase readability"). 'continue' increases readability by increasing statement locality and emphasizing linear execution. 'break' causes execution flow jumps and shatters loop encapsulation. - Dustin Getz
(1) the loop you used as an example just begs to be refactored. At first glance, I would reasonably presume that when the loop is finished, x==0 and all indexes 0<i<Xinitial have been processed. To realize that this is not the case, I would have to parse the entire loop. - Dustin Getz
See McConnell, Steve. Code Complete, Second Edition. Microsoft Press © 2004. Chapter 16.2: Controlling the Loop. - Dustin Getz
(1) @Dustin, I didn't conclude that breaks increase readability (although they may) and there are situations within a loop where you know it should exit so there's no point in a continue since it re-checks the condition. Breaking means you mak have to put less clutter in the condition itself. - paxdiablo
@Dustin, your reasonable presumption would be wrong - think of a loop that does 5 long-running subasks each time through, and you need to check a flag set by another thread and exit immediately after the current subtask. Contrived, I know, but well within the bounds of possibility. - paxdiablo
(1) Pax - that code sample is horrible. Multiple breaks and returns are a sign of poor logic and methods that are not well designed. They are certainly less maintainable. The Uni is doing us all a service. - Tim
(19) @Tim, apparently the rest of SO disagrees. Dogmatic adherence to rules without thinking is a sign of a weak mind (I'll take your ad hominem attack and raise you another :-). I would rather have readable code than the massive multi-condition stuff I've seen from some people following these rules. - paxdiablo
(2) @Dustin, continue and break are dual concepts (continue is goto to the start of the loop, break is goto beyond the end of loop). They are equivalent in terms of readability. - J S
(4) Depending on the setting I can see where insisting on explicit exit conditions may be important in a Univ. class. If you're driving the point home about loop invariants and exit conditions -- both important CS topics -- insisting on it may be vital. Once you've progressed beyond the learner level, you can know when it's ok to break the rules, no pun intended. - tvanfosson
"but there is nothing unreadable about this piece of code. You can clearly see all flows of control at a single glance": I think I must have a problem with my eyes then ;-) I think that sample is a good point to prove @S.Lott's statement. - 0xA3
(2) @0xA3, if you can't follow code flow across an 11-line code segment (with a maximum break-to-closing-brace distance of 8 lines), I really think there is something wrong with your eyes :-) - paxdiablo
How would you ever know after your loop whether process (y); or process (z); have been executed for the last value of x? Or how to know what happened to the last x processed? Something? Or SomethingElse as well??? Of course, in a real scenario, you might exactly want to have such a behavior (and maybe you would have method names that are self-evident), but it is hard to see what is really going on especially if those methods have side-effects. As Dustin already said, such a loop just screams to be refactored, at least it is a code smell. - 0xA3
(1) The point is, @0xA3, that you don't care. Otherwise it would have been written differently. That was the whole point of the paragraph starting "But you should keep in mind ...". If you're not concerned about a specific post-condition then it doesn't matter at all what state things are in on loop exit. - paxdiablo
I definitely recommend reading the Code Complete section on exiting loop bodies. McConnell demonstrates how easy bugs can be introduced. He concludes: "Multiple breaks don't necessarily indicate an error, but their existence in a loop is a warning sign, a canary in a coal mine that's gasping for air instead of singing as loud as it should be." - 0xA3
I've read McConnel (I only have the first edition unfortunately) but it doesn't appear his stance has changed based on that link you posted. He does not advocate, like the OP's lecturers, that you avoid break altogether. Rather he says to be careful which is sensible. If you're just taking up C coding, you shoud be careful - it's like using a chainsaw. But maybe 30-odd years of cutting code has made me confident (or complacent). I think we may just have to leave it there, I'm getting too old to change my ways now :-) - paxdiablo
(2) @Dustin Getz: "I would reasonably presume that when the loop is finished, x==0 and all indexes 0<i<Xinitial have been processed.". You're wrong. When the loop is finished, then the only thing that you should presume is that it did its job, nothing more. Most my loops won't process all indices. They just work, and stop as soon as necessary. For loops processing all indices, I would use a foreach-like feature, which makes the "break" a mandatory instruction. - paercebal
@0xA3 I hate to burst your bubble but for/while loops can have multiple exit conditions, even without the 'break' statement. Ex "while(true && programmers == "write code" && people == "disagree"){ KeepArguingAboutPointlessSemantics(); }". The 'break' statement just allows you to move those conditional statements into the body for better readability. - Evan Plaice
(4) @paxdiablo Nicely demonstrated. The only thing that I'd add is, 'break' statements are not like 'goto' statements because they respect structure (Ie, respect the scope rules) therefore it's difficult/impossible to write spaghetti code using them. I think people attribute 'spaghetti' to mean 'sloppy' code. It takes a little experience in BASIC to really understand what spaghetti code is (and how bad code can get when you aren't provided a respectable structure to work with in the first place). - Evan Plaice
(1) For all the "break is evil" dogmatics, here's a version without them: while (x != 0 && ((y = doSomethingWith (x))? (process(y), true) : false) && ((z = doSomethingWith (x))? (process(z), true) : false)) x--; ... and Evan is right about the difference between break and goto. People don't understand Dijkstra's point, or the context of the time he was writing in, when people were coding in assembler and in FORTRAN II with its 2-way branching if statement. - Jim Balter
1
[+148] [2010-04-17 06:19:22] Norman Ramsey

I've been told by professors and peers at my university that using the break statement is bad practice

Come visit Tufts and our professors will tell you otherwise.

The arguments against break boil down to one principle: break requires non-local reasoning, and a language with break requires a much more complicated semantic framework than a language without break. (For the experts in the room, instead of using simple tools like predicate transformers or Hoare logic, you have to reach for something like continuations, or at the very least, a context semantics.)

The problem with this argument is that it puts simplicity of semantics ahead of programmers' real needs. There are lots of programs with natural loops that have more than one exit. Programming languages need to support these loops in a way that is more effective than introducing extra Boolean variables to govern the control flow.

For some expert testimony on the value of multiple exits from control-flow constructs, I recommend two papers:

  • Structured Programming With goto Statements [1] by Donald E. Knuth. Don goes to great length to explain why certain kinds of gotos should be allowed in Pascal. Most of these gotos are equivalent to some form of break, which hadn't quite been invented yet when Don wrote the paper.

  • Exceptional Syntax [2] by Nick Benton and Andrew Kennedy. The topic may seem unrelated, but throwing an exception is a nonlocal exit, just like break. (In Modula-3, break was defined to be an exception.) It's a great paper showing how language designers need to be more imaginative in designing syntax to support multiple exits.

If you really want to annoy your professors, ask them if the return statement is bad practice. If they say "no", you've got them: "But isn't return a control operator, just like break? And isn't it the case that introducing return intro a structured program creates all the same semantic difficulties that introducing break does?" Watch them squirm.

Is using the break statement bad practice?

No. The break statement is a valuable tool in your toolbox, just like return or exceptions. Like other tools, it can be misused, but there is nothing inherently bad about it, and in fact the break statement is pretty easy to use in sane and sensible ways.

Your professors should learn some more powerful semantic methods that can tame the break statement.

[1] http://pplab.snu.ac.kr/courses/adv_pl05/papers/p261-knuth.pdf
[2] http://research.microsoft.com/en-us/um/people/akenn/sml/exceptionalsyntax.pdf

(11) +1: very well written answer. - Thorbjørn Ravn Andersen
I totally agree with you, but on the subject of "return" I'd argue that a programmer can self-discipline to only ever use "return" as the last statement in a function, and this simplifies the analysis. You could self-discipline to only use break as the last statement in a loop, but that would be pointless because you could just incorporate it into a loop continuation condition. So practically, "using break" isn't analogous to "using return", it's more analogous to multiple returns. The anti-break people are almost certainly also anti-multiple-return, and anti-goto. And anti-denormed-data. - Steve Jessop
(10) ... so they might not squirm at all, they might say "no, you shouldn't be using return as an arbitrary control operator, and IMO languages should not have a return statement, they should have named return parameters and drop off the end of the function, and/or return the last expression evaluated". At which point you give up and go write some assembler for a while to remind yourself that goto may be "wrong", but it's fun ;-) - Steve Jessop
(1) +1 excellent answer. I would also say there is a subtle difference between programmer's needs and programmers programmers wants. So while it is true that the dogmatic statement "all breaks are bad" puts semantics ahead of needs, I think it is vitally important to distinguish the need versus the want. The latter leads to sloppy coding that results from the programmer not thinking hard enough about the algorithm. I think it is more helpful to say "use breaks carefully", which establishes that breaks have a valid use; you just have to know when to use them correctly. - Vivin Paliath
(1) @Vivin I'm with you---and so is Don Knuth. This is basically what he says in his paper, with lots of convincing examples. @Steve: it might interest you that Lua permits return only in the last statement of a block. But that's a lot more powerful than the last statement of a whole function. I pity the programmer who can't write if type(t) ~= "table" then return nil, "table expected" end at the beginning of a long function. - Norman Ramsey
(2) @Norman: yes indeed. For most programs you aren't even going to think about proving correctness formally, so there's no sense at all it making things easier for code analysers at the cost of making it harder for programmers. Those "no early return" people want us either to write lots of nested if statements to handle those early-out conditions, or else to write a whole bunch of functions: deal_with_something, deal_with_something_that's_definitely_a_table, deal_with_a_non_empty_table, and so on, and have each call the next. It's a consistent world-view, just not a very pragmatic one... - Steve Jessop
@Steve Food for thought... Isn't a function/sub-procedure just another form of structured goto though? :) - Evan Plaice
@[Evan Plaice] All control statements are just structured gotos. - Lambda Fairy
2
[+43] [2010-04-17 03:33:36] Joel

This comes from the idea that there should be one way IN a method and one way OUT. Same with loops. I've had some instructors tell me that I shouldn't use more than one return or any break/continue because it creates "spaggetti code" and it's hard to follow the path. Instead, they say to set a flag and use an if statement rather than just break out. I completely disagree with this idea. I think in a lot of cases having more than one return or a break/continue statement is much more readable and easier to follow.


(8) The benefit of multiple returns over one return is exaggerated in shorter functions (which we should all strive for anyway), but I can definitely see the logic in avoiding multiple return statements once your function starts approaching, say, 20 lines or more. If you can't visualize all exit points on the same screen full of code, it can definitely get confusing. - Chris
An exit point implies that you're done. It doesn't matter where that takes place in a method, or whether it takes place more than once. When you're done, you're done. If you have missing logic, it simply means you put your break statement in the wrong place. - Robert Harvey
(6) @Chris: Except for very rare cases, a function that's longer than a single screenful is indicative of a larger problem than multiple exit points. - Billy ONeal
I don't get why this answer is getting so many upvotes... it sidesteps the issue that there are valid uses for flags and that flags can make the code more readable. Too many times I find people abusing break in a for loop when a nicely-written while loop would suffice. - Vivin Paliath
(2) @Vivin Paliath: Personally, I despise flags. You have to hunt for what they refer to, and what they refer to is completely specific in the context of that program. break always does the same thing. While I agree break should be avoided, I don't think it's a case of break versus flags. Excessive use of break is usually indicative of a larger problem, like infinite loops, which are much much worse than break. - Billy ONeal
(16) The thing is, if-break guards are often much, much more readable than long, multiple-nested ifs caused by input validation. - Kyte
@Billy that's why I give my flags very nice names ;) I guess it depends on how you write your code. I think you're right about breaks vs. flags. Each of them have their uses, and I've also seen loops that have been written without breaks that contain a LOT of flags. Quite terrible, although that may be a sign of a bigger problem. I don't think breaks by themselves are bad; they just have a potential for abuse. - Vivin Paliath
@Kyte: I'd even consider that a code smell. The input validation things that would cause such loop pain should be extracted into separate methods. - Billy ONeal
@Billy ONeal: It doesn't even have to be too complex. Clause A checks the input parameters. Then you load a database entry to check for, say, if it's not a duplicate entry, which would be Clause B. Two levels of if-indentation is already more than enough to make coding a severe pain. Return guards make such things far simpler. (My brains are slightly addled. When I said "break guard" I actually referred to return guards. Apologies) - Kyte
@Billy: That's something you've got to balance against the increased complexity that comes from splitting up the code into more separate pieces. I tend to think of these things as forces pointing one way or the other, with different balances between the two dependent on the particular details of the code in question. - Donal Fellows
3
[+27] [2010-04-17 04:52:26] Stephen C

I've been told by professors and peers at my university that using the break statement is bad practice

The first thing to realize is that many of those people have never actually been professional software engineers, and never had to work on a large code base written by many developers over many years. If you do this, you learn that simplicity, clarity, consistency and use of accepted idioms are more important in making code maintainable than dogma like avoiding break/continue/multiple return.

I personally have no problems reading and understanding code that uses break to get out of loops. The cases where I find a break unclear tend to be cases where the code needs to be refactored; e.g. methods with high cyclomatic complexity scores.

Having said that, your professors have the right motivation. That is, they are trying to instill in you the importance of writing clear code. I hope they are also teaching you about the importance of consistent indentation, consistent line breaking, consistent white space around operators, identifier case rules, meaningful identifiers, comments and so on ... all of which are important to making your code maintainable.


(10) "many of those people have never actually been professional software engineers" <-- +1 for that - Billy ONeal
People I have come across SESE are those with the most real experience in industries. - Tom Hawtin - tackline
@Tom - yes, but you may also have noticed that the SESE dogma is more prevalent among people who started out in IT 30+ years ago. That was the time when "structured programming" was the new way. (Don't get me wrong. Structured programming was a good thing, especially for FORTRAN / COBOL / BASIC / C programmers. But in those days it tended to be followed very dogmatically ...) - Stephen C
4
[+25] [2008-10-19 12:25:09] Greg

I don't see any harm in using break - it's useful and simple. The exception is when you have a lot of messy code inside your loop, it can be easy to miss a break tucked away in 4 levels of ifs, but in this case you should probably be thinking about refactoring anyway.

Edit: IMHO it's much more common to see break in a while than a for (although seeing continue in a for is pretty common) but that doesn't mean it's bad to have one in a for.


5
[+25] [2008-10-19 12:47:08] Greg B

I think it's a completely pompous and ridiculous rule to enforce.

I often use break within a for loop. If i'm searching for something in an array and don't need to keep searching once I find it, I will break out of that loop.

I agree with @Konrad Rudolph above, that any and all features should be used as and when the developer sees fit.

In my eye, a for loop is more obvious at a glance than a while. I will use a for over a while any day unless a while is specifically needed. And I will break from that for if logic requires it.


I suppose it would be pompous to correct the spelling of ridiculous. - gbarry
6
[+20] [2008-10-19 12:25:46] Richard Harrison

My rule is to use any and all features of the language where it doesn't produce obscure or unreadable code.

So yes, I do on occasion use break, goto, continue


7
[+14] [2008-10-19 12:41:09] Rob Walker

I often use break inside a for loop.

The advantage of a for loop is that the iterator variable is scoped within the expression. If a language feature results in less lines of code, or even less indented code then IMHO it is generally a good thing and should be used to improve readability.

e.g.

for (ListIt it = ...; it.Valid(); it++)
{
  if (it.Curr() == ...)
  {
     .. process ...
     break;
   }
}

Rewriting this using a for loop would require several more lines, and leak the iterator out of the scope of the loop.

(Pedantic points: I only want to act on the first match, and the condition being evaluated isn't suitable for any Find(...) method the list has).


(1) I agree with this usage. It is clearer to understand. The for loop controls the iterator. The only thing I might add is that this same pattern will often be used with continue as well. - Cervo
8
[+13] [2008-10-19 14:04:09] Cervo

Break is useful for avoiding nesting. Also there are many times that it is useful to prematurely exit a loop. It also depends on the languages. In languages like C and Java a for loop basically is a while loop with an initialization and increment expression.

is it better to do the following (assume no short circuit evaluation)

list = iterator on something
while list.hasItem()
  item = list.next()
  if item passes check
      if item passes other check
            do some stuff
            if item passes other check
                  do some more stuff
                  if item is not item indicating end of list
                        do some more stuff
                  end if
            end if
       end if
   end if
end while

or is it better just to say

while list.hasItem()
     item = list.next()
     if check fails continue
       .....
     if checkn fails continue
     do some stuff
     if end of list item checks break
end while

For me it is better to keep the nesting down and break/continue offer good ways to do that. This is just like a function that returns multiple times. You didn't mention anything about continue, but in my opinion break and continue are of the same family. They help you to manually change loop control and are great at helping to save nesting.

Another common pattern (I actually see this in university classes all the time for reading files and breaking apart strings) is

currentValue = some function with arguments to get value
while (currentValue != badValue) {
    do something with currentValue
    currentValue = some function with arguments to get value
}

is not as good as
while (1) {
    currentValue = some function with arguments to get value
    if (currentValue == badValue)
       break
    do something with currentValue
}

The problem is that you are calling the function with arguments to create currentValue twice. You have to remember to keep both calls in sync. If you change the arguments for one but not the other you introduce a bug. You mention you are getting a degree in software engineering, so I would think there would be emphasis on not repeating yourself and creating easier to maintain code.

Basically anyone who says any control structure is bad and completely bans it is being closed minded. Most structures have a use. The biggest example is GOTO. A lot of people abused it and jumped in the middle of other sub procedures, and basically jumped forwards/backwards all over the code and gave it a bad name. But GOTO has its uses. Using GOTO to exit a loop early was a good use, now you have break. Using GOTO to centralize exception handling was another good use. Now you have try/catch exception handling in many languages. In assembly there is only GOTO for the most part. And using that you can create a disaster. Or you can create our "structured" programming structures. In truth I generally don't use GOTO except in excel VBA because there is no equivalent to continue (that I know of) and error handling code in VB 6 utilizes goto. But I still would not absolutely dismiss the control structure and say never...

Unfortunately the reality is that if you don't want to fail, you will have to avoid using break. It is unfortunate that university doesn't have more open minded people in it. To keep the level of nesting down you can use a status variable.

status variable = true
while condition and status variable = true
  do stuff
  if some test fails
    status variable = false
  if status variable = true
     do stuff
  if some test fails
     status variable = false
  ....
end while

That way you don't end up with huge nesting.


+1 giving good examples - Kaii
Best answer here by far. But people should go back and read Dijkstra's comment about goto to understand that his context was languages in which goto was the only control structure. break was an element of structured programming that replaced the unconstrained, indiscriminate goto. - Jim Balter
9
[+12] [2010-04-17 03:35:25] Duck

Makes switch statements a whole lot easier.


(8) break is definitely fine in switch statements. Does anyone disagree with this? - Personman
(3) I can count on one hand the number of switches I have seen that intentially omitted all break statements. - Duck
(1) @Duck, I would need a hand with many fingers to do the same - hhafez
(1) @hhafex, which switch statements do you think of without any breaks at all? - Thorbjørn Ravn Andersen
(1) @Duck, @Thorbjørn Sometimes falling through cases is quite useful, to be fair. - James
(1) @Thorbjørn: Duff's device is a switch with no breaks. Or you might write something like switch(version) { case 1 : migrate_to_version_2; case 2 : migrate_to_version_3; ... case latest-1: migrate_to_version_latest; case latest: printf("done"); }, if the actions to "migrate" are too dissimilar for a loop to be of any use. - Steve Jessop
(1) While falling through case is possible in C, it should be avoided. After all, 99.9% of cases missing break are bugs, and it is quite a common bug. That is why Lint gives a warning for missing break. - PauliL
(1) @Steve, I understand Duff's device (clever hack) and I sort of understand your suggestion. But hhafez said he needed MANY fingers, and I frankly do not believe that the loop unrolling trick is needed in THAT many places. - Thorbjørn Ravn Andersen
(1) @Thorbjørn: Yeah, I'm not sure whether he's using fingers to count the number of patterns which involve such a switch statement, or the number of times he has seen such a statement written. It's all trickery really, my example could have been written if (version == 1) migrate_to_2; if (version == 2) migrate_to_3; etc, assuming of course that "migrate_to_N" modifies version. My switch statement is close to something that you might write in assembly, but that doesn't necessarily make it a good thing, just makes it easily comprehensible to that kind of programmer. - Steve Jessop
(1) @PauliL: "99.9% of cases missing break are bug" - not in my code, don't know about yours ;-p. I think I approach writing switches in two different ways: if I'm using it purely to enumerate some cases, then I'll write the break statements first. If I'm using it as a sort of restricted state machine then I'll either comment the fall-through, or else fall-through almost everywhere, with break becoming an unusual termination much like break in a loop. The difference is obvious (to me) in context. Lint can keep its nose out. - Steve Jessop
(1) Wouldn't it probably be better if there was some kind of fallthru keyword to denote (to the compiler and to humans, something comments can't do) that you intentionally don't want to break in a switch statement? "Explicit is better than implicit." - badp
(1) @badp, better idea: have case(foo) {stuff();} (note lack of :) equiv to case(foo): {stuff();} break;. (Could complicate parsing though.) - David X
(1) @David: so you'd want to put at the beginning of a case "block" what happens after it ends? - badp
(1) @badp: well, if you think about it, that's the difference between if and while as well: break vs continue at the end of block (in the sense of "what do we do next"). Could also add a new keyword (select?) for the whole switch block, but case(foo){} avoids invalidating any existing code (except maybe some really insane macro abuse). - David X
10
[+8] [2008-10-19 14:35:27] Sol

I would argue your teachers' prohibition is just plain poor style. They are arguing that iterating through a structure is a fundamentally different operation than iterating through the same structure but maybe stopping early, and thus should be coded in a completely different way. That's nuts; all it's going to do is make your program harder to understand by using two different control structures to do essentially the same thing.

Furthermore, in general avoiding breaks will make your program more complicated and/or redundant. Consider code like this:

for (int i = 0; i < 10; i++)
{
   // do something
   if (check on i) 
        break;
   // maybe do something else
}

To eliminate the break, you either need to add an additional control boolean to signal it is time to finish the loop, or redundantly check the break condition twice, once in the body of the loop and once in the loop's control statement. Both make the loop harder to understand and introduce more opportunities for bugs without buying you any additional functionality or expressiveness. (You also need to hoist the declaration of i out of the loop's control structure, adding another scope around the entire mess.)

If the loop is so big you cannot easily follow the action of the break statement, then you'd be better off refactoring the loop than adding to its complexity by removing the break statement.


why not use while(i < 10 && !(check on i))? It gets rid of the if and the break. I can tell the exit condition from the loop definition. - Vivin Paliath
(1) Because the loop is actually doing something (thus the comment "// do something") between the i < 10 check and the check on i. - Sol
11
[+8] [2010-04-17 03:56:02] stakx

Edit: First of all, sorry if this answer seems somewhat long-winded. However, I'd like to demonstrate where my expressed opinion about the break statement (bold text at the end of my answer) comes from.


One very good use case for the break statement is the following. In loops, you can usually check a break condition in either of two places: Either at the loop's beginning, or at the loop's end:

while (!someBreakCondition)
{
    ...
}

do
{
    ...
} while (!someBreakCondition)

Now, what do you do when, for some reason, you cannot put your break condition in either of these places, because e.g. you first need to retrieve a value from somewhere before you can compare it to some criterion?

// assume the following to be e.g. some API function that you cannot change:
void getSomeValues(int& valueA, int& valueB, int& valueC)
//                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//                           out parameters
{
    ...
}

while (true)
{
    int a, b, c;
    getSomeValues(a, b, c);
    bool someBreakCondition = (b = ...);

    ...   // <-- we do some stuff here
}

In the above example, you could not check for b directly inside the while header, because it must first be retrieved from somewhere, and the way to do that is via a void function. No way to put that into the while break condition.

But it may also be too late to wait until the end of the loop for checking the condition, because you might not want to execute all the code in the loop body:

do
{
    int a, b, c;
    getSomeValues(a, b, c);
    bool someBreakCondition = (b = ...);

    ...   // <-- we want to do some stuff here, but only if !breakCondition
} while (!someBreakCondition);

In this situation, there's two approaches how to avoid executing the ... when someBreakCondition is true:

1) without using break

    ...  // as above

    if (!someBreakCondition)
    {
        ...  // <-- do stuff here
    }
} while (!someBreakCondition);

2) with break

    ...  // as above
    if (someBreakCondition) break;

    ...  // <-- do stuff here
} while (true);

Your professor would obviously favour option 1) (no break). I personally think that's not a nice option, because you have to check the break condition in two places. Option 2) (use break) resolves that problem. Admittedly though, option 1) has the advantage that it becomes more easily recognisable through code indentation under what condition the ... code will execute; with option 2), you have to go up in your source code to find out that program execution might never actually get to some place further down.

In conclusion, I think using or not using break in this (quite frequent) scenario it really comes down to personal preference about code legibility.


You also mentioned that break might circumvent clean-up code, and that it might be problematic for this reason. In languages that have the try..finally idiom, this is not so. You can specify clean-up code with a finally block, and it will always get executed (provided there's no previous abnormal program termination). - stakx
The bigger problem here is using an infinite loop in the first place, not break. - Billy ONeal
I agree. The infinite loop is another drawback of the break approach. For this reason, some prefer writing for (;;) instead of while (true) because this "reeks" less of an infinite loop. But it's clearly a potential source for bugs. -- The other thing is, in programming languages that support a more functional programming style, one can often get rid of loops completely and replace them through constructs such as map projections or reductions. With the C language though, this would probably not work in a neat way. - stakx
@Billy and stakx: I disagree. Why would infinite loop be a problem, is infinite loop is what is needed? On the other hand, a loop that has break in the middle is no more infinite loop than a loop which has the break at the beginning or end. - PauliL
(1) @PauliL: Because 99% of people are not writing programs that never terminate. If you need an infinite loop, that's a sign you have not thought about your termination condition enough. A loop without a well defined termination point is a million times worse than break. - Billy ONeal
(1) @Billy: If the loop has break, it is not an infinite loop. It terminates when the break is executed. So where is the problem? If infinite loop is needed (such as tasks in multi tasking OS's), failing to have infinite loop is a big problem. - PauliL
@PauliL: If you are in that situation, then an infinite loop is fine. But that's the 0.5% case. Unless you want your program to never terminate, you should not have a loop whose condition is to never terminate. - Billy ONeal
(1) @Billy: As I explained, a loop that has end contidion in the break statement does have end condition and it is not an infinite loop. - PauliL
The basic problem here is that there's no language I'm aware of that has a good structure for a loop where you decide in the middle of the code whether to continue or not. Thus we end up with all sorts of suboptimal approaches like while (true). The real world contains problems of this form, you simply have to decide which is the least offensive answer. - Loren Pechtel
(1) @Loren The basic problem is that people don't know their computer science. Bith Dijkstra and Knuth wrote about the "loop-and-a-half" problem. You can read more about this at www-cs-faculty.stanford.edu/~eroberts//papers/SIGCSE-1995/… - Jim Balter
12
[+6] [2008-10-21 19:43:13] Jon Ericson

break is an extremely valuable optimization tool and is especially useful in a for loop. For instance:

-- Lua code for finding prime numbers
function check_prime (x)
   local max = x^0.5;

   for v in pairs(p) do
      if v > max then break end;

      if x%v==0 then 
         return false
      end 
   end
   p[x] = true;
   return x 
end

In this case, it isn't practical to set up the for loop to terminate at the right moment. It is possible to re-write it as a while loop, but that would be awkward and doesn't really buy us anything in terms of speed or clarity. Note that the function would work perfectly well with out the break, but it would also be much less efficient.

The huge advantage of using break rather than refactoring into a while loop is that the edge cases are moved to a less important location in the code. In other words, the main condition for breaking out of a loop should be the only condition to avoid confusion. Multiple conditions are hard for a human to parse. Even in a while loop, I'd consider using break in order to reduce the number of break conditions to just one.


I'm aware that this is not the most efficient prime checker, but it illustrates a case where break really helps both performance and readability. I have non-toy code that would illustrate it, but require more background information to set it up.


13
[+5] [2008-10-19 12:33:41] Konrad Rudolph

If it's a guideline that's enforced by the corrector, you don't really have the choice.

But as guidelines go, this one seems to be excessive. I understand the rationale behind it. Other people argue (in the same vein) that functions must only have one single exit point. This may be helpful because it can reduce control flow complexity. However, it can also greatly increase it.

The same is true for break in for loops. Since all loop statements are basically idempotent, one kind can always be substituted for any other. But just because you can doesn't mean that this is good. The most important coding guideline should always be “use your brain!”


How exactly are "all loop statements idempotent"? - Draemon
I think what he meant to say is you can substitute one for the other. This is not always true because some language looping constructs are super restricted so the more general construct can do things the restricted one cannot do. But C while/for can be subbed. Do is harder to sub.... - Cervo
But you can substitute do with an early exit pattern (using break) or an additional firstExecution variable and a while (firstExecution || condition) and the first line in the loop being firstExecution = false. Again do is simpler :) - Cervo
much harder to substitute a while loop by the vb for x = 1 to n construct. Or foreach construct. You'd have to keep changing x or the list with an if statement....Some languages don't let the list in foreach be changed.... - Cervo
@Draemon: they're all Turing complete. Every for loop can be rewritten as a while loop and vice versa. Even foreach loops can always be substituted or used as substitute. CS 101. - Konrad Rudolph
idempotent: "that word ... I do not think it means what you think it means" -- Inigo Montoya - Zac Thompson
(1) @Zac: I don’t get the reference so it might be a joke but I am quite certain that “idempotent” means exactly what I think it means. - Konrad Rudolph
"Other people argue (in the same vein) that functions must only have one single exit point. " -- Those people are incompetent. Far better code results from having the return, break, or continue statement as lexically close to the condition test as possible, and from doing the condition test as soon as possible. - Jim Balter
@Jim Depends on the language. In languages such as C where you need to do explicit resource clean-up, having a single exit point can greatly decrease a function’s complexity. In modern languages the guideline is of course poison. - Konrad Rudolph
@Konrad First, the argument was "must" only have a single exit point, but the vast majority of C functions do not need to do any resource cleanup. Second, in C the resource should be allocated and freed in a simple routine that calls another routine to perform the algorithm, and that routine can return at will. - Jim Balter
14
[+5] [2008-10-19 13:00:51] Stefan Rådström

In a way I can understand the professor's point of view in this matter, but only as a way to teach the students how to solve problems in a (some kind of) standard fashion. Learn to master these rules, then you are free to break against them as you wish, if that will make the code easier to understand, more effective, or whatever.


great insight! it's the same with music and art, to know the rules just so that you know better why you're breaking them - DarenW
15
[+5] [2008-10-21 21:54:48] Ken Paul
  1. While in school, follow the defined guidelines. Some guidelines are arbitrary, and exist primarily for consistency, ease of grading, or to keep within the teacher's limited understanding. The best balance between maximizing learning and maximizing grades is to follow the guidelines.
  2. In the real world, the balance shifts to maximizing benefit for your employer. This usually requires a focus on readability, maintainability and performance. Since programmers rarely agree on what maximizes these qualities, employers typically attempt to enforce even more arbitrary guidelines. Here the stakes are keeping your job and possibly climbing to a leadership position where you can actually influence the standards.

16
[+5] [2010-04-17 03:50:50] Vivin Paliath

I tend to shy away from breaks. Most of the time, I've found that a flag is sufficient. Of course, this doesn't mean that breaks are always bad. They do have their uses. Your teachers and peers are right about it being a "get out of jail free" card. This is because breaks, like gotos, have a very high potential for abuse. It's often easier to break out of a loop than to figure out how to structure the loop correctly. In most cases, if you think about your algorithm and logic, you will find that you do not need a break.

Of course, if your loop ends up having a whole bunch of exit conditions, then you need to either rethink your algorithm or use breaks. I tend to really sit and think about my algorithms before deciding to use a break. When you write code and you get that voice in your head that says There has to be a better way than this!, you know it's time to rewrite your algorithm.

As far as loops are concerned, I use for loops when want to iterate over a collection of items with a known bound and I know that there are no other exit conditions. I use a while loop when I want to run a series of statements over and over again until something happens. I usually use while loops if I am searching for something. In this case, I don't break out of the while. Instead, I use a flag because I think the while loop in its entirety reads more like English and I can tell what it's doing. For example:

int i = 0;
while(i < size && !found) {
   found = (value == items[i]);
   i++;
}

The way I read that in English in my head is While i is lesser than the total count of items and nothing is found. That, versus:

int i = 0;
for(int i = 0; i < count; i++) {
   if(value == items[i]) {
      break;
   }
}

I actually find that a bit harder to read because I can't tell immediately from the first line what the loop is actually doing. When I see a for loop, I think Ok, I'm running over a known list of items no matter what. But then I see an if in that loop and then a break inside the if block. What this means is that your loop has two exit conditions, and a while would be a better choice. That being said, don't do this either:

int i = 0;
while(i < count) {
   if(value == items[i]) {
      break;
   }
   i++;
}

That's not much better than the for with the break. To sum it all up, I'd say use break as a last resort, and only if you are sure that it actually will make your code easier to read.


While I agree that your example showcases a bad use of break; the reason has nothing to do with break. The reason is a loop like that should be broken into it's own function, and you should use return instead of break there. - Billy ONeal
@Billy, the example was a bit contrived. I would say that using a return out of a loop isn't that good of an idea either. Unless it is a very short function. - Vivin Paliath
@Vivin Paliath: That would be the point. The for should be extracted into it's own 5 or 6 line function. If the body of the for is too long for that, than the body of the for should be extracted as well. Too bad there's no std::find in C or Java :( - Billy ONeal
@Billy I agree. I tend to extract things like that into functions/methods anyway. I still don't like returning out of loops since that's the same as breaks :) Maybe it's a personal preference. Also agree with you about the find! - Vivin Paliath
+1 for various comments around here and for having a South Park avatar. - Billy ONeal
@Billy haha thanks! :) - Vivin Paliath
I think your example is excellent. But my opinion is the complete opposite. Without the break, the loop is (the tiniest bit) less efficient because it requires another evaluation of the conditional. - sje397
17
[+5] [2010-04-17 12:26:56] Steve Jessop

Those who claim that it is bad say that it's a "get out of jail free card" and that you can always avoid using it.

They may claim that, but if that's their actual argument then it's nonsense. I'm not in jail, and as a programmer I'm not in the business of avoiding things just because they can be avoided. I avoid things if they're harmful to some desired property of my program.

There's a lot of good discussion here, but I suggest that your professors are not idiots (even if they're wrong), and they probably have some reason in mind when they say not to use break. It's probably better to ask them what this is, and pay attention to the answer. You've given your "guess", but I propose to you that they would state their case better than you state it. If you want to learn from them, ask them to explain the exact details, purpose, and benefits of their coding guideline. If you don't want to learn from them, quit school and get a job ;-)

Admittedly, I don't agree with their case as you've stated it, and neither do many others here. But there's no great challenge in knocking down your straw-man version of their argument.


18
[+5] [2010-06-26 05:42:10] Jonah

Here is a good use-case in PHP:

foreach($beers as $beer) {
    $me->drink($beer);

    if ($me->passedOut) {
        break; //stop drinking beers
    }
}

for ($i=0; !$me->passedOut && $i<count($beers); ++$i) { $me->drink($beers[$i]); } :) - cHao
19
[+4] [2008-10-19 12:44:41] OregonGhost

I also learned at uni that any functions should have only a single point of exit, and the same of course for any loops. This is called structured programming, and I was taught that a program must be writable as a structogram because then it's a good design.

But every single program (and every single structogram) I saw in that time during lectures was ugly, hardly readable, complex and error-prone. The same applies to most loops I saw in those programs. Use it if your coding guidelines require it, but in the real world, it's not really bad style to use a break, multiple returns or even continue. Goto has seen much more religious wars than break.


"any functions should have only a single point of exit" I talked about this in a blog post: blogs.conchango.com/anthonysteele/archive/2008/07/14/… - Anthony
(1) "I also learned ..." -- "learned" should only be used to describe facts; for the sort of irrational dogma you're referring to, say "I was also taught ..." - Jim Balter
20
[+4] [2008-10-19 14:50:34] Jay Bazuzi

Most of the uses of break are about stopping when you've find an item that matches a criteria. If you're using C#, you can step back and write your code with a little more intent and a little less mechanism.

When loops like this:

foreach (var x in MySequence)
{
	if (SomeCritera(x))
	{
		break;
	}
}

start to look like:

from x in mySequence
where x => SomeCriteria(x)
select x

If you are iterating with while because the thing you're working on isn't an IEnumerable<T>, you can always make it one:

    public static IEnumerable<T> EnumerateList<T>(this T t, Func<T, T> next)
    {
        while (t != null)
        {
            yield return t;
            t = next(t);
        }
    }

(1) Your first two examples don't do the same thing. The first one iterates over every x in MySequence and stops on the first one for which SomeCriteria() is true. The second one iterates over all x in MySequence for which SomeCriteria() is true. I think you meant "continue" in the first example. - Robert Rossney
21
[+4] [2008-10-21 19:05:23] DJClayworth

The rule makes sense only in theory. In theory for loops are for when you know how many iterations there are, and while loops are for everything else. But in practice when you are accessing something for which sequentil integers are the natural key, a for loop is more useful. Then if you want to terminate the loop before the final iteration (because you've found what you are looking for) then a break is needed.

Obey your teacher's restriction while you are writing assignments for him. Then don't worry about it.


"Obey your teacher's restriction while you are writing assignments for him. Then don't worry about it." - that's the most intelligent thing I've heard for a while. It works for management as well in the real world :-) Upvoting.. - paxdiablo
"The rule makes sense only in theory." -- This is a nonsensical concept of "theory". The rule only makes sense if one ignores various cases and conditions, making it erroneous theory. See www-cs-faculty.stanford.edu/~eroberts//papers/SIGCSE-1995/… - Jim Balter
22
[+3] [2008-10-19 12:52:26] tvanfosson

I understand the issue. In general you want to have the loop condition define the exit conditions and have loops only have a single exit point. If you need proof of correctness for your code these are invaluable. In general, you really should try to find a way to keep to these rules. If you can do it in an elegant way, then your code is probably better off. However, when your code starts to look like spaghetti and all the gymnastics of trying to maintain a single exit point get in the way of readability, then opt for the "wrong" way of doing it.

I have some sympathy for your instructor. Most likely he just wants to teach you good practices without confusing the issue with the conditions under which those practices can be safely ignored. I hope that the sorts of problems he's giving you easily fit into the paradigm he wants you to use and thus failing you for not using them makes sense. If not, then you get some experience dealing with jerks and that, too, is a valuable thing to learn.


23
[+3] [2008-10-19 13:09:02] Pulsehead

You are still in school. Time to learn the most important mantra that colleges require of you:
Cooperate and Graduate.

It's good that your school has a guideline, as any company you work for (worth a plugged nickel) will also have a coding guideline for whatever language you will be coding in. Follow your guideline.


It's true but the other golden rule in a company is everything is up for negotiation. I often fight against stupid coding guidelines. And sometimes I get them changed when I show enough example. Or keep mentioning to the boss issues that crop up from the guidelines and increase the timeline... - Cervo
even in school everything is up for negotiation (well, it may be harder than in real world but it can still be done). When I was in school (long ago) my University had such rules. One of them was never never use for (;;)... however I remember using it when the subject asked explicitely for an infinite loop (when writing an OS scheduler). I was pointed out but I successfully argued than while (1) was quite worse. - kriss
24
[+3] [2009-03-17 16:05:05] Thomas Padron-McCarthy

Another view: I've been teaching programming since 1986, when I was teaching assistant for the first time in a Pascal course, and I've taught C and C-like languages since, I think, 1991. And you would probably not believe some of the abuses of break that I have seen. So I perfectly understand why the original poster's university outlaws it. It is also a good thing to teach students that just because you can do something in a language, that doesn't mean that you should. This comes as a surprise to many students. Also, that there is such a thing as coding standards, and that they may be helpful -- or not.

That aside, I agree with many other posters that even if break can make code worse, it can also make it better, and, like any other rule, the no-breaks rule can and (sometimes) should be broken, but only if you know what you're doing.


25
[+3] [2010-04-17 03:33:03] Samuel

I don't think there is anything wrong with using breaks. I could see how using a break can be seen as skipping over code but it's not like a goto statement where you could end up anywhere. break has better logic, "just skip to the end of the current block".

slightly off topic...(couldn't resist)
http://xkcd.com/292/


Goto statement does not "end up to anywhere". It goes to exactly one specified location which is given in the goto statement, just like procedure call. - PauliL
if it's your code you know where you put the label, but it could be anywhere in your code. what if someone else had to maintain your code? i suppose they could do a quick search and figure it out. - Samuel
26
[+2] [2008-10-19 14:24:50] Cervo

@lodle.myopenid.com

In your answer the examples do not match. Your logic is as follows in the example in the equation and example A in your answer:

while X != 0 loop
   set y
   if y == 0 exit loop
   set z
   if z == 0 exit loop
   do a large amount of work
   if some_other_condition exit loop
   do even more work
   x = x -1

example b:
while X != 0 loop
  set y
  if y == 0
    set z
  elseif z == 0
    do a large amount of work
  elseif (some_other_condition)
    do even more work
  x--

This is absolutely not the same. And this is exactly why you need to think about using break.

First of all in your second example you probably meant for the if var == 0 to be if var != 0, that is probably a typo.

  1. In the first example if y or z is 0 or the other condition is met you will exit the loop. In the second example you will continue the loop and decrement x = x - 1. This is different.
  2. You used if and else if. In the first example you set y, then check y, then set z then check z, then you check the other condition. In the second example you set y and then check y. Assuming you changed the check to y != 0 then if y is not 0 you will set z. However you use else if. You will only check Z != 0 (assuming you changed it) if y == 0. This is not the same. The same argument holds to other stuff.

So basically given your two examples the important thing to realize is that Example A is completely different from Example B. In trying to eliminate the break you completely botched up the code. I'm not trying to insult you or say you are stupid. I'm trying to overemphasize that the two examples don't match and the code is wrong. And below I give you the example of the equivalent code. To me the breaks are much easier to understand.

The equivalent of example A is the following

  done = 0;
  while X != 0 && !done {
    set y
    if y != 0 {
      set z
      if z != 0 {
        do large amount of work
        if NOT (some_other_condition {
          do even more work
          x = x - 1
        } else
          done = 1;
      } else
        done = 1;
    } else
      done = 1;
  }

As you can see what I wrote is completely different from what you wrote. I'm pretty sure mine is right but there may be a typo. This is the problem with eliminating breaks. A lot of people will do it quickly like you did and generate your "equivalent code" which is completely different. That's why frankly I'm surprised a software engineering class taught that. I would recommend that both you and your professor read "Code Complete" by Steve McConnell. See http://cc2e.com/ for various links. It's a tough read because it is so long. And even after reading it twice I still don't know everything in it. But it helps you to appreciate many software implementation issues.


Your refactoring still isn't quite right - in fact it probably loops for ever as is y == 0, x isn't changed. You probably need an extra immediateExit boolean, or else set X to 0 if Y (or Z) is 0. - Douglas Leeder
sorry you are correct.......See it applies to me. I took more time to think than the original and I still screwed it up... - Cervo
I quoted the relevant part of CC2E below: the gist of that chapter is don't write garbage loops in the first place. the loop in the +30 post is indefensible garbage - and the fact that it's so hard to get rid of the breaks only proves it. - Dustin Getz
"the fact that it's so hard to get rid of the breaks only proves it" -- uh, no. Someone should read www-cs-faculty.stanford.edu/~eroberts//papers/SIGCSE-1995/… - Jim Balter
27
[+2] [2008-10-21 19:48:44] Dustin Getz

break typically does make loops less readable. once you introduce breaks, you can no longer treat the loop as a black box.

while (condition)
{
   asdf
   if (something) break;
   adsf
}

cannot be factored to:

while (condition) DoSomething();

From Code Complete:

A loop with many breaks may indicate unclear thinking about the structure of the loop or its role in the surrounding code. Excessive breaks raises often indicates that the loop could be more clearly expressed as a series of loops. [1]

Use of break eliminates the possibility of treating a loop as a black box1. Control a loop's exit condition with one statement to simplify your loops. 'break' forces the person reading your code to look inside to understand the loop's control, making the loop more difficult to understand. [1]

  1. McConnell, Steve. Code Complete, Second Edition. Microsoft Press © 2004. Chapter 16.2: Controlling the Loop.

You can factor it by introducing a boolean stop variable that's initialized to false. The condition then becomes condition && !stop and the break becomes stop=true;continue; (modulo adding braces as necessary). It doesn't make things clearer. - Donal Fellows
agreed; DoSomething must then be aware of its calling context which breaks encapsulation - Dustin Getz
Note that he's saying excessive breaks and he's right. Anything with a high complexity is almost certainly a bad answer. - Loren Pechtel
Functions are black boxes. People expecting loops to be black boxes are using the wrong construct. Even then, it is simply false that using break eliminates the possibility of treating a loop as a black box, as the existence of pure functions with loops with internal return statements proves. - Jim Balter
28
[+2] [2008-10-21 21:19:47] Robert Rossney

Out of curiosity, I took a little tour of the codebase I'm working on - about 100,000 lines of code - to see how I'm actually using this idiom.

To my surprise, every single usage was some version of this:

foreach (SomeClass x in someList)
{
   if (SomeTest(x))
   {
      found = x;
      break;
   }
}

Today, I'd write that:

SomeClass found = someList.Where(x => SomeText(x)).FirstOrDefault();

which, through the miracle of LINQ deferred execution, is the same thing.

In Python, it would be:

try:
   found = (x for x in someList if SomeTest(x)).next()
except StopIteration:
   found = None

(It seems like there should be a way to do that without catching an exception, but I can't find a Python equivalent of FirstOrDefault.)

But if you're not using a language that supports this kind of mechanism, then of course it's OK to use the break statement. How else are you going to find the first item in a collection that passes a test? Like this?

SomeClass x = null;
for (i = 0; i < SomeList.Length && x == null; i++)
{
   if (SomeTest(SomeList[i]))
   {
      x = SomeList[i];
   }
}

I think break is just a wee bit less crazy.


ListIt it = ...; while (it.Valid() && test(*it)) it++; Process(it); - Dustin Getz
even better (python or any other language): extract method and use early return. tomayko.com/writings/cleanest-python-find-in-list-function. - Dustin Getz
Nice. I like both of those. - Robert Rossney
Or in python without exception: from itertools import chain; found = chain((x for x in list if test(x)), (None,)).next() - kriss
29
[+2] [2010-04-17 03:36:43] Personman

In general, anything that makes execution jump around and isn't a function call has the potential to make your code more confusing and harder to maintain. This principle first gained widespread acceptance with the publication of Dijkstra's Go To Statement Considered Harmful [1] article in 1968.

break is a more controversial case, since there are many common use cases and is often pretty clear what it does. However, if you're reading through a three- or four-deep nested loop and you stumble upon a break (or a continue), it can be almost as bad. Still, I use it sometimes, as do many others, and it's a bit of a personal issue. See also this previous StackOverflow question: http://stackoverflow.com/questions/57600/continue-considered-harmful

[1] http://www.u.arizona.edu/~rubinson/copyright_violations/Go_To_Considered_Harmful.html

(2) The bigger problem is the 3 or 4 deep nested loop, not break. Garbage code is garbage code. It matters not what kind of statements are used to produce that code. - Billy ONeal
The break statement is an element of structured programming, which was a response to Dijkstra's observation. break simply does not have the problems of goto that Dijkstra pointed out, any more than return does. - Jim Balter
30
[+2] [2010-04-17 03:39:38] Brian Gianforcaro

I believe your professor's are just trying (wisely) to instil go coding practices in you. Break's, goto's, exit(), etc can often be the cause behind extraneous bugs throughout code from people new to programming who don't really have a true understanding of what's going on.

It's good practice just for readability to avoid intruding possible extra entrances and exit's in a loop/code path. So the person who reads your code won't be surprised when they didn't see the break statement and the code doesn't take the path they thought it would.


Why the downvote? - Brian Gianforcaro
-1: I'm sorry, but putting breaks and gotos even in the same league is misleading. The only thing you cite is that these statements perform jumps. By that logic, any type of jump statement -- for, while, and do..while belong in that category. Also, I don't see what exit has to do with anything given that it results in immediate termination of the program. - Billy ONeal
Break is not an "extra exit", it is the normal way to exit at middle of the loop. - PauliL
@PauliL: it's an extra exit if the loop already had an exit, without the break statement. If you would modify "never use break", to "never use break to create a loop with more than one exit", then that's still a stronger prohibition on break than most programmers observe if they use break at all. - Steve Jessop
31
[+2] [2010-06-26 06:15:53] Pavel Radzivilovsky

In the real world, few people care about style. However, break from loop is an okay thing by strictest coding guidelines, such as that of Google, Linux kernel and CppCMS.

The idea of discouraging break comes from a famous book, Structured Programming by Dijkstra http://en.wikipedia.org/wiki/Structured_programming that was the first one to discourage goto. It suggested an alternative to goto, and suggested principles which might have misled your professors.

Since then, a lot changed. Nobody seriously believes in one point of return, but, the goto - a popular tool at the time of the book - was defeated.


(1) Unless you are programming in assembly ... - Noctis Skytower
Or any language with no scopes/structure. - Pavel Radzivilovsky
32
[0] [2008-10-19 13:15:55] Lodle

Ok, thanks for the answers its good to know what other people think. I use it in special occasions as pointed out already but most times using break can be avoided.

The example given was:

while (x != 0) {
    // Do some calculations to set y.
    if (y == 0) break;
    // Do some calculations to set z.
    if (z == 0) break;
    // Do a large amount of work.
    if (some_other_condition) break;
    // Do even more work.
    x--;
}

which can be refactored using else if statements

while (x != 0) {
    // Do some calculations to set y.
    if (y == 0)
    // Do some calculations to set z.
    else if (z == 0) 
    // Do a large amount of work.
    else if (some_other_condition)
    // Do even more work.
    x--;
}

(2) See my comment below that starts @lodle.myopenid.com. Your refactoring is completely wrong. I give you the correct refactoring in pseudocode on the bottom. This is why it is dangerous to completely eliminate some structure from your repertoire. - Cervo
(2) Agreed - the refactoring is bogus. The loop condition becomes much more complex, and you have to do 'if (y == 0) { ...nothing... } else { // Do some calculations to set z, etc. }' and the code nests horribly fast. - Jonathan Leffler
(1) the original loop is bogus. it needs refactoring, and not with if statements - the whole problem needs to be deconstructed. add some code where those comments are and it will be completely impossible to understand (debug, test, verify, maintain) - Dustin Getz
The original loop defined the algorithm and thus cannot be "bogus". What is completely bogus here is a) Lodle's code, which is just horribly wrong, and b) Dustin's point-evading comment. - Jim Balter
33
[0] [2008-10-21 21:09:12] dwelch

See the popular response above. The rules you are taught are garbage, hardly worth the time to speak them much less write them down and talk about them.

What is important here is that to complete your degree you have to follow a set of programming guidelines. follow those guidelines. In the real world it is quite possible that you will find jobs with programming rules or guidelines (unfortunately culled from the garbage taught at school).

1) code within the guidelines for that job/task

2) rise to a level of power within that job/task that you can change the programming guideline to something that is not garbage.

For the most part those programming guidelines are there to aid the professor in grading your work. Also to teach you a certain subject. If you use calculus to solve an algebra problem in an algebra class I would hope you fail the class. You are there to learn algebra.

Once you have the time to compile your programs to assembler and inspect what is REALLY going on, you will see the light. The break, continue, and goto are all directly mapped to jumps/branches, almost one to one, these are the cleanest coding structures you can find. While, for, etc these are abstract and bulky. Count how many "gotos" (branches) it takes in the assembler to avoid multiple return paths in a function. Count how many branches it takes if you use breaks or returns or continues throughout your code. Notice how much cleaner the code is with breaks and returns and continues? gotos are sketchy, although the single best operation in a programming language, because of the focus it has had from universities, not all compilers handle it properly. You may have to avoid it only because the programmer writing the compiler really doesnt understand them. breaks and continues are very good though, use them as much as possible (once you graduate).


(1) "The break, continue, and goto are all directly mapped to jumps/branches ... While, for, etc these are abstract and bulky." I would write it as "while, for, etc have high-level abstraction and elegance". Why would I want to be closer to the hardware? - Dustin Getz
@Dustin You may want it if you want to understand how the code will be compiled. You may not want to. Not everyone would choose the red pill. Still I'm not arguing for abusing of breaks and continue wheever possible as @dwelch proposed. It smells too much like premature optimization to me. - kriss
34
[0] [2009-03-17 14:56:17] J S

Some languages enforce break usage. For example, in Python (a very readable language, isn't it?), it is a valid idiom the following:

while True:
  .. do something ..
  if loop_condition: break

This code is equivalent to do-while loop in other languages (which is hard to express in Python because of the block syntax constraints). It has also an obvious advantage that you can just move loop condition anywhere else in the loop. So I would argue this is really more readable than having two (or three) different syntaxes depending on whether your loop starts, ends with condition or has it in the middle.


In a previous company we had guidelines about python... and a while loop was something to avoid whenever possible as much more bug prone than for loops on containers... however when you loop on containers break is really handy for early exit. - kriss
35
[0] [2010-04-17 03:45:22] Roderick Taylor

If you keep this rule, then the point of exit is only where the loop condition is tested. Not having to sift through code looking for breaks makes the code a lot more readable. If everybody kept this rule then the code written is much easier to maintain and the world is a better place.

Rules are meant to be broken, so for short loops I don't see what's wrong with break. (I like to keep my loops 5-10 lines short)


If the loop isn't short methods should be extracted to make it short. - Billy ONeal
36
[0] [2010-04-17 04:14:38] T.R.

Continue and Break jump to specific places - they don't create spaghetti code. On the other hand, continue and break can obscure the fact that not all of the code in the loop will necessarily be executed. In many cases, a better solution is to write other code to avoid the need to use them, such as using the Null Object Pattern when iterating over a collection.

Focusing specifically on the loop - circumstances should dictate.
The alternative for continue is often to enclose the contents of the whole loop in a conditional - I would argue in favor of using continue to avoid the extra nesting.
Sometimes a while loop can be structured to eliminate the need for a break, such as by using a flag variable - that should be preferred, unless doing so makes the intent of the code less apparent. Specifically, a break would be preferable to setting a flag and then using continue to avoid processing the rest of the loop.


(1) As an aside, the "goto is bad" statement is somewhat overused IMO. If you look at old code, e.g. old BASIC code with line numbers and tons of GOTO because functions and subprocedures didn't yet exist, you can see where this statement about goto's badness comes from. But that doesn't mean that today, every single use of goto is inherently bad. -- I agree though that it's rarely needed if the program code is structured well. - stakx
-1 for the same reasons as Brian Gianforcaro's answer. break and continue should not be put under the same hammer as goto. - Billy ONeal
@Billy ONeal - I'll remove the reference to Goto, because perhaps I wasn't being clear enough - I meant to contrast them with Goto - I think they're perfectly fine, I just think that in most situations it's better to engineer the solution so that you don't need them. - T.R.
@stakx - Goto certainly isn't inherently bad, particularly when other constructs aren't available, but I think there's some use in keeping the structure of code predictable. In the end, if you need it, you need it. - T.R.
@T.R.: Okay, removed downvote. - Billy ONeal
37
[0] [2010-04-17 04:41:54] rmarimon

Breaks have their place. I keep getting back to it when linearly searching through a list. Snippets like this are not uncommon

SomeClass found = null;
for(SomeClass c : listOfSomeClass) {
  if (sometest(c)) {
    found = c;
    break;
  }
}
if (found != null) {
  System.out.println("there is something here");
}

The boolean flag alternative is way less expressive and confusing and can not be used easily with the forEach construct.


@rmarimon: That's still bad. The foreach should be extracted into it's own method which gets rid of both the flag and the break statement. - Billy ONeal
(1) @Billy: "should" is way too strong. He has a 10 line function here, which finds something and reports it to a listener (stdout). You would replace it with two functions, adding at least 3 lines, and perhaps requiring a closure to pass sometest to the find function: I'm not sure whether it's intended here as a function name or a place-holder. I guess either you find "return" inherently more pleasing than "break", or else you value single responsibility over brevity, or both. Either way, someone else can legitimately disagree, or find a case where your principle makes the code "worse". - Steve Jessop
Certainly he "should" consider writing a generic "find" function, though, I don't disagree with that at all. But it doesn't take much for such generic functions to be more unwieldy than just writing the loop - for instance in C++, std::for_each is almost useless in practice and std::find_if rarely gives much improvement, simply because in C++03 any kind of "closure" substitute is non-local and verbose and generally more trouble than it's worth for a simple expression. - Steve Jessop
38
[0] [2010-04-17 05:03:47] Billy ONeal

Okay -- I know I'm posting an answer to a question that already has a large number of responses, but I think this is important.

Is using the break statement bad practice?

Yes and no. There is absolutely no language feature that should never be used. If there was a feature that should never be used, that feature would not be in the language. Language designers aren't stupid, and the fact that break and continue have existed in some form in pretty much every C derived programming language shows that language designers still believe it's a useful feature.

Consider the goto statement, which has been removed in languages like Java, which provide language features that (almost) completely replace the need for goto.

Like all language features, break has very good uses, and very bad ones. There is one very specific use for break: You are iterating over a loop and have encoutered some condition that forces you to stop looping. That's it. That's the only reason you should ever see it.

In that sense, I disagree with your Professor. There are legitimate uses of break. It'd not be there otherwise.

However, I will agree with your Professor that relying on break is usually indicative of bad design. Consider the following (admittedly contrived) implementation of strlen:

size_t strlen(const char * str)
{
    size_t idx = 0;
    for(;;idx++) {
        if (!*str)
            break;
    }
    return idx;
}

What is the problem here? Is it break? I don't think so. The problem is the infinite loop. I have to read the body of the loop in order to understand its termination condition. That's bad. it should instead be something like:

size_t strlen(const char * str)
{
    const char * begin = str;
    while(*str) str++;
    return str - begin;
}

Break used as a replacement for the loop's termination condition is a sign that you have not thought about the termination condition well enough.

Use break when it's appropriate. But consider excessive use of it a code smell. Consider extracting your loop containing the break and using a return statement instead. While the return has some of the same problems as the break, forcing yourself to use return instead of break here encourages you to keep these kinds of loops short and localized -- as they should be anyway.

Note that all of this applies to flag variables as well -- break statements and flag variables are essentially the same with regards to the above advice. They are indicative that something else is wrong with your design if they are used excessively.


Of course it is bad practice to use break and mis-use for in a situation that clearly requires while. But it is equally bad practice to use all kind of tricks to avoid using break in a situation where break is the natural construct. Using return in the middle of loop, however, is something that should always be avoided. - PauliL
@PauliL: I strongly strongly disagree. Return in the middle of a loop is correct -- but the loop should be it's own well defined function. - Billy ONeal
I strongly strongly strongly disagree. Return in the middle of loop is a very bad idea. Creating a separate function for every loop is really bad idea. A loop should be in the context it belongs to, so that you can see what is going on and you can use the variables of that function. In addition, creating a separate function for every loop creates enormous amount of unnecessary functions, making the code both inefficient and difficult to maintain. - PauliL
@PauliL: I'm not saying every loop needs to have it's own function, I'm saying every loop that requires a stop-in-the-middle semantic deserves it's own function. - Billy ONeal
39
[0] [2010-04-17 09:56:05] Clifford

Never say never, and if your intent is clear and you can make a reasoned argument for its use and why it is better than not using it then why not?

However it is a good idea to keep such advice in mind, because there are good reasons for it, even if there are also (a few) situations when it could be disregarded; and even if your teachers cannot tell you the reasons, and are just repeating received wisdom without truly understanding why it may be important. Spaghetti code is not the most compelling argument.

Consider a large and complex loop body, it is useful to know that the loop will always exit from one point (the bottom); that way you only have one place (the loop condition) to consider when debugging the loop. Say you place a breakpoint after the loop, and then wish to determine why it exited, observing the loop condition expression and its variables is all you need to know. If the loop contains breaks, you may have much more to consider post-mortem to determine where it came from. Even worse in this case would be the use of return or goto to exit the loop since not only will you not know directly where it came from but also where it will go to; so you will need multiple breakpoints to trap loop exit.

Even for short, simple loops, the use of a break may be ill-advised since under maintenance, the loop may not remain small and simple. This may be the case on a large, long-term project with multiple developers for example. It is seldom an issue in the kind of academic exercises you might be involved with at college, but it may be wise to get into good habits, even when it does not matter.

The point is that the person debugging or maintaining the code may not be the person who wrote it in the first instance; or you may be debugging it long after you wrote it. Making it easy to debug and maintain in these situations is useful, and what looked reasonable to you when you were intimately familiar with the code, may look like spaghetti in the long term, even for relatively simple constructs; "someone else's code" is almost always incomprehensible on first sight, and maintaining simplicity and adhering to a few rules and standards can help immensely in these situations.


40
[0] [2010-06-29 08:44:02] Eiko

There might be other things to consider that don't directly relate to the programming task. At some point the student might have to formally prove the correctness of their algorithm (i. e. Hoare logic [1]). And you may well run out of your tools to do this task.

I still don't think that this formal verification is any useful in real world, but someone probably think it's worthwhile to teach for other reasons.

[1] http://en.wikipedia.org/wiki/Hoare_logic

41
[-1] [2008-10-21 19:56:18] Dustin Getz

Most examples of "good" breaks in this thread can be expressed cleaner without break.

@Pax Diablo

i agree with your premise ("do what increases readability"). i disagree with your conclusion ("breaks increase readability"). 'continue' increases readability by increasing statement locality and emphasizing linear execution. 'break' causes execution flow jumps and shatters loop encapsulation.

the loop you used as an example just begs to be refactored. At first glance, I would reasonably presume that when the loop is finished, x==0 and all indexes 0 < i < Xinitial have been processed. To realize that this is not the case, I would have to parse the entire loop.

@Jay Bazuzi, you said this was good:

foreach (var x in MySequence)
        if (SomeCritera(x))
                break;

this is equivalent and more elegant:

var x;
while (valid(x) and not SomeCriteria(x)) 
    next x;

@Quarrelsome, you commented:

foreach(Thing stuff in things) 
{ 
  if(stuff.Id == thingImLookingFor) { // do stuff break; }
}

this is more readable:

var thing; 
while (valid(x) and not IsThingImLookingFor(x)) next x; 
do stuff

@Rob Walker, you said this was the best:

for (ListIt it = ...; it.Valid(); it++)
{
  if (it.Curr() == ...)
  {
     .. process ...
     break;
  }
}

how about this (exact same paradigm as the above two refactorings)

ListIt it = ...;
while (it.Valid() && it.Curr() != ...) it++;
Process(it);

@Cervo - one of your examples is weak, but the other example seems ok - the one necessary use of 'break' so far in this thread.

while list.hasItem()
     item = list.next()
     if check fails continue
       .....
     if checkn fails continue
     do some stuff
     if end of list item checks break
end while

i don't understand exactly what you mean by if end of list item checks break, but it seems that it isn't even necessary - the loop would naturally finish because there are no more items. if you don't mean to loop until the list is empty, why not use if NOT end of list item checks as your loop condition? no break, preserves loop encapsulation (the entire loop body could be factored into another function, with 'continue' mapped to early returns.

the one good use of 'break' in this thread that I can see

@Cervo - this is a good example.

currentValue = some function with arguments to get value
while (currentValue != badValue) {
    do something with currentValue
    currentValue = some function with arguments to get value
}

versus the better:

while (1) {
    currentValue = some function with arguments to get value
    if (currentValue == badValue)
       break
    do something with currentValue
}

I could only come up with this response, which is specific to a subset of languages, i think it's worse than your while(1).

while ( (input = getch()) != ‘\0’)
      Process(input);

(1) @Dustin: composite tests are a pitfall and really easy to understand wrongly, especially with negated conditions. If you wonder why you can see it well explained in Damian Conway's Perl Best Practises (and readability is especially an issue with Perl). Hence your while loops using basically composite tests being "more elegant" or more "readable" is far from being obvious, quite the opposite [and I did not downvoted you]. - kriss
foreach(Thing stuff in things) { if(stuff.Id == thingImLookingFor) { // do stuff break; } } -- good code, stuff is local in scope to the loop. var thing; while (valid(x) and not IsThingImLookingFor(x)) next x; do stuff -- awful, incompetent code that gives one variable three different names and makes it global to the loop. - Jim Balter
42
[-5] [2010-04-17 12:52:49] PauliL

Maybe you are mixing break with return? Especially since you are talking about cleanup code. There is no cleanup code in loops that break could skip.

There is no reason to avoid break, and I have not heard any professor or textbook to recommend avoiding breaks. However, it is often recommended that multiple return points in a function should be avoided.

Break is an important construct in C. For example switch...case construct requires break on each branch. (Higher level languages implicitly add break at end of each case branch so you do not need to write them.)

In loops, break is needed when you need to exit from middle of the loop. (Of course you should not use break to exit at the beginning or end of loop, since there are other constructs for that purpose.)

If you use tricks (e.g. flags and if's) to avoid using break, the resulting code is more unreadable. Using tricks instead of natural method is always bad.

Using return is more debatable. It is often said that each function should only have a single exit point. Return at the middle of function makes the code more difficult to follow, and may cause some cleanup code to be skipped. A bad mistake is to use return inside a loop, it should always be avoided.

However, there are some cases when return near the begin of function is a good idea. For example when you can instantly check the parameters and notice that there is no reason to run the actual function. In such situation, return near the begin of function can be both more efficient and more readable than multiple nested if's.


(2) -1 for "each function should only have a single exit point". That's true in C, but it's certainly not true in more modern languages such as C++/Java/C#/... - Billy ONeal
@Billy ONeal: You are dead wrong. All programming courses and textbooks teach you to have only one exit point in each function (althoug this may be overkill, as I told above). Shame on you for downwoting. - PauliL
(3) Strange that in my collection of 9 programming books that I can't find one. - Billy ONeal
43
[-23] [2008-10-19 13:09:41] S.Lott

In the real-world, I look at every break statement critically as a potential bug. Not an actual bug, but a potential bug. I challenge the programmers I work with on every break statement to justify its use. Is it more clear? Does it have the expected results?

Every statement (especially every composite statement) has a post-condition. If you can't articulate this post-condition, you can't really say much about the program.

Example 1 -- easy to articulate.

while not X:
   blah blah blah
assert X

Pretty easy to check that this loop does that you expected.

Example 2 -- harder to articulate.

while not X:
   blah
   if something I forgot: 
      break
   blah blah
   if something else that depends on the previous things:
      break
   blah
assert -- what --?
# What's true at this point?  X?  Something?  Something else?
# What was done?  blah?  blahblah?

Not so easy to say what the post-condition is at the end of that loop. Hard to know if the next statements will do anything useful.

Sometimes (not always, just sometimes) break can be bad. Other times, you can make the case that you have loop which is simpler with a break. If so, I challenge programmers to provide a simple, two-part proof: (1) show the alternative and (2) provide some bit of reasoning that shows the post-conditions are precisely the same under all circumstances.

Some languages have features that are ill-advised. It's a long-standing issue with language design. C, for example, has a bunch of constructs that are syntactically correct, but meaningless. These are things that basically can't be used, even though they're legal.

break is on the hairy edge. Maybe good sometimes. Maybe a mistake other times. For educational purposes—it makes sense to forbid it. In the real world, I challenge it as a potential quality issue.


(4) Personally, I think you make a valid point but your conclusion is wrong. There's a whole class of cases where premature leaving a for loop makes sense and is the best solution for the problem. But then, these can easily be justified and would thus pass your challenge. - Konrad Rudolph
I agree with Konrad Rudolph. Also I think you should look at my second post that starts @Lodle as well as the poster's post with an answer to his own question. As you will see his refactoring was completely wrong. I think the equivalent code is much uglier than the break version.... - Cervo
Admittedly you could have used a status variable but many of the same close minded people who say never to a control structure say no to status variables. So my version is the safest version. - Cervo
(3) I would also add that BREAK is a substitute for a valid use of GOTO which is early loop termination which eliminates duplication which leads to a whole class of bugs. See my first post for the early loop exit pattern. Think reading file, getting fields from cursor, regex on string, etc... - Cervo
@Konrad Rudolph: Bravo -- if they're easily justified and obviously simpler, they're fine. But I insist on seeing the proof. - S.Lott
@Cervo: I agree -- if you can prove the two arrive at the same post-condition AND the break version is simpler, you have an argument for using break. But BOTH parts have to be true. In too many cases, the post-condition gets very murky with poorly-design break statements. - S.Lott
(2) @Cervo: I've never seen the "no status variable" rule. Some status variables are poorly defined, and don't have an obvious meaning in the post-condition. In that case, it's as bad as a poor break condition. But good status variables are a helpful optimization. - S.Lott
In the common case where you are looping over objects and accessing them by sequential integer a for loop is simpler than a while (less likely to get the increment wrong for one thing). If you terminate early you need a break. - DJClayworth
(1) @DJClayworth: When I see a break stmt, I ask to see the post-condition and I ask to see that the alternative really is more complex. It's often the case that the post-condition is obvious and the non-break version is more complex. But I always ask to see the details. - S.Lott
(2) This is "multiple exit points == bad" dogma. The more fundamental issue is always the readability of code and in some cases a break; statement is more readable. - Quibblesome
Konrad, "There's a whole class of cases where premature leaving a for loop ... is the best solution" would you post one of these cases? - Dustin Getz
(59) foreach(Thing stuff in things) { if(stuff.Id == thingImLookingFor) { // do stuff break; } } - Quibblesome
(7) var thing; while (not IsThingImLookingFor(x)) next x; do stuff - Dustin Getz
@Qarrelsome: multiple exit points is bad when you can't summarize them with a logical assertion about the state of the program. If you can summarize the multiple exits, then they're fine. - S.Lott
@Dustin - the foreach exits once the list has expired.... yours either requires an additional condition on the while or a break inside the loop :P. Apples and Oranges IMO. - Quibblesome
@Quarrelsome, so you trade an explicit condition for a break. its obvious to me that the explicit condition is more readable. - Dustin Getz
Apples and Oranges, Dustin. I like reading Manga, one of my best friends hates manga. Is all manga good or bad? It doesn't matter, we just differ in opinion. There are much more worthy pursuits in the aims of reaching readable code nirvana than "multiple exit points". - Quibblesome
(4) @kevchadders: A lot of people like to think of "break" as less confusing that a proper terminating condition. In general, it's very hard to construct a proof around loops with breaks. Loops with provable breaks can usually be refactored into something simpler. - S.Lott
@S.Lott - I don't understand your last objection. What exactly you mean by "harder to prove"? You can refactor any loop with break into a loop with additional condition variable. How's that going to help in proving things? - J S
@J S: If the break decisions and various bits of processing are interleaved, it's very hard to write the post-condition that succinctly defines what the net effect of the loop is. This isn't about condition variables, it's about writing a final post-condition. - S.Lott
@S.Lott: So what you are saying is that the 2nd example of yours should be split into 3 different loops, each without break and going only over those elements that got over break? Is that what you would do? Because I still want to do code the same thing. - J S
(1) @S.Lott: I forgot - is the example you give supposed to be an iteration over some collection/sequence, or is it supposed to be an event control loop? I understand you may have a valid point there, but I don't understand your example, it's too general. - J S
@J S: My 2nd example is one loop with a really complex end-state. It probably something like (X or (Something and blah) or (Something else and blah and not Something)). Breaks make it hard to write a post-condition (or assert statement) that summarizes the loop as a whole. - S.Lott
@S.Lott: So how would you refactor the 2nd example, if you want to retain the functionality? The first loop is less complex, so obviously it is easier to reason about it. - J S
(1) @J S: It's intentionally hard to refactor the 2nd example. It requires a total rethinking of the algorithm to eliminate the partial results and incremental testing. Often there have to be some function and test condition variables added; the initial invariant is usually wrong in cases like this. - S.Lott
(3) Why all the down votes? Makes a reasonable point. I am trying to understand what dogma has been violated here. - Gordon Potter
(2) @Gordon Potter: The downvotes are simply a refusal to acknowledge that break statements can add complexity if they're used poorly. It's easy to prove that they add complexity. The consequences of break are unpleasant to contemplate. Hence the downvotes. - S.Lott
(14) @SLott : I downvote not because I do not believe break statement can add complexity but because I consider your answer is from an academic point of view, not from a real world one. break is in my toolset and I use it in some case. For instance I believe it's very readable when the logical exit of a loop is in the middle of it to avoid doing the same treatment twice either before or after the loop. It also is part of some derecursivation scheme I use. I also take it as a lesser evil as return in the middle of a function for error management If exceptions are no available (old C without 'try'). - kriss
@kriss: I maintain real-world code with real-world break statements that are -- in the real world -- made insanely crazy by a real-world break statement that's poorly thought-out. The only way to reason out correct behavior is to revert to a "pure" while loop to determine what was supposed to happen. All in the real world. All real code that I really had to maintain and I really couldn't parse because the break statement was incomprehensibly placed. - S.Lott
(1) @SLott: if you are saying that break can be a good tool to obfuscate code, I agree. But I do not believe you would be better off with ''return''s in the middle of your code instead of ''break''s. I understand you can be sorry when maintening a poorly written code base (I'm also doing it with a C++ application). Going on that way I may say I'm seeing every while loop in this code as potential bugs... because a while loop is much harder to get right than a for loop. As a matter of fact I see more bugs caused by poorly thought while loops than from for loop containing extra exit with breaks. - kriss
@Slott: I also do not really understand what you mean by "really couldn't parse" are you hinting of automated code rewriting or automated proof of program ? - kriss
(1) @kriss: Parse means parse. It doesn't mean automated code rewrite or proof. It means parse. Read, comprehend, understand. Parse the code is to understand it's meaning. Break (often) makes it impossible to parse the code and understand the meaning. And yes, while loop is harder to get right than the more narrowly constrained for loop. Add break and it's almost impossible to get right. - S.Lott
(6) @S.Lott -- if you need to prove correctness, I would agree wholeheartedly. An easily checked loop invariant and exit condition are essential. Most programs, fortunately, don't have a need for a rigorous proof and proof by example (TDD/unit tests) is usually sufficient. There are times when it's appropriate -- early termination of a search, for instance, when you find the element you're looking for -- that don't result in hard to reason loop invariants. I suspect that the kind of code one writes may have a large impact on whether you view break as a potential evil or a god-send. - tvanfosson
@tvanfosson: "proof by example (TDD/unit tests) is usually sufficient" if -- and only if -- you have a reasonable level of confidence. It's all too easy to write dumb loops with poorly-designed break conditions in which one cannot have any level of confidence. Break is a potential bug. Most folks who say a "clear or simple break is a good thing" are right. If the break is obvious, it's good. That means you looked at it as a potential bug and reasoned that it was correct. If it isn't obvious, it's a potential bug. - S.Lott
@S.Lott -- with TDD, you'd introduce the break either because it caused your test to pass (and maintained all other passing tests) or because you refactored to something better while maintaining your passing tests. Obviously, this is dependent on writing sufficient tests to cover your required behavior. - tvanfosson
@tvanfosson: the point is that break is often the root cause of problems because you can't easily determine what the post-break condition is. If you have tests that do that, then you must also have reason to believe the work. If you have that, then you must have done some reasoning about the break. That's goodness. Merely saying "break is acceptable" misses the point that the reasoning required to be confident in a break is sometimes quite hard, and difficult to test. If it's easy, you've designed well. - S.Lott
@S.Lott it's reasonable to ask that a programmer understand the postconditions. But asking that break be banned sometimes forces you to explicitly code postconditions where there is no need. For example: for (int i=0;i<n;i++) { if (matching(array[i])) {matched=array[i];break;}} . The only postcondition we are interested in is the state of 'matched'; you are forcing the coder to articulate the postcondition of i. - DJClayworth
@DJClayworth: "asking that break be banned"? Who suggested that? I missed it. I certainly did not for a moment suggest it be banned. Only examined. - S.Lott
(1) Wow I don't understand the downvotes on this. I belong to the school of "use break sparingly". break definitely has its uses. I think I have such an aversion to it because I've mostly seen break abused rather than used. So when I think I need to use a break, I think really hard to see if I need to use it. I don't think the OP supports a dogmatic statement of "don't use break". I think he's trying to say "use break carefully". - Vivin Paliath
(3) @Vivin Paliath: Thinking of break as a potential bug appears to scare many people. Don't know why a potential bug is so terrifying. But there it is: 47 downvotes because I said there was a "potential" bug in their software. - S.Lott
@S. Lott I don't get why people say "generalized rules are bad". It's a general rule because it applies most of the times. The reason it's there is because people abuse break rather than use them. It's often an excuse for poor coding rather than actually figuring out if it is ok to use a break in a specific situation. It's the same reason why goto is considered harmful. Does it mean it doesn't have uses? Not at all. But part of being a good programmer is known when to use the right tools in your toolset. - Vivin Paliath
(1) @Evan Plaice: Where did I suggest "Banning 'break' from switch statements"? I didn't suggest banning anything. Nor did I mention switch. I can't see that in the posting. The use of break in switch is an irrelevant quirk of C that I never mentioned. - S.Lott
(2) +1 I think some people should read (and understand) an answer before voting. I find it answers the question very well. (Sadly, I can't upvote it, because I accidentally clicked again on the up-arrow and now my right to vote on this answer is locked...) - Simon Lehmann
(1) It's a bad answer, because it's a hypothetical answer. Sure, if break is only used poorly then you shouldn't use it - which goes without saying. By contrast, the foreach loop Quibblesome wrote is just fine - as are other examples. In other words, the rule is not a law, just something to keep in mind - avoid breaks when doing so confuses the post condition. - Eamon Nerbonne
(1) Dustin Getz's alternative is pseudocode but easy to get buggy in a real language - you need o initialize x manually, you've changed variable scope (in a bad way-more global), and you've changed object lifetime. - Eamon Nerbonne
@Evan Plaice: asserts can easily be turned off for production code, and so they needn't affect performance. assertions are also a form of documentation. Frankly I think sprawling tests (and frequent breaks) are the mark of a careless programmer while assertions are the mark of a careful programmer. - Artelius
@Artelius: putting an assert after a loop verifying you exited the loop seems somewhat overkill. This test is useless. The loop is there for a reason. Let it do its job the best way it can. If you want to assert things, then try to assert meaningful data, not the fact the loop was exited through a break. - paercebal
Geez, it's not example code. It's just supposed to illustrate the fact that "I'm confident in the state of the program at this point in time." - Artelius
@Evan Plaice: "agree to disagree here"? What? You completely changed my posting. I never said any of the things you're disagreeing with. Your statement is incomprehensible. I asked -- and I ask again -- "Where did I suggest "Banning 'break' from switch statements"? How can we "disagree" when I never said such a thing? What are you talking about? Please explain what part of my answer gave you that impression so that I can fix it. - S.Lott
@Evan Plaice: "I was referring to your comments". You introduced "switch" into the comments. It is not mentioned prior to your use of the word. (1) "Where did I suggest "Banning 'break' from switch statements"? (2) Since I cannot figure out what we are talking about, I can't see what you are trying to "sway" me to. I never mentioned switch. What "opinion" are you talking about? I suggested break is a potential bug. How is that "religious"? I cannot parse your comments at all. Please clarify. - S.Lott
(1) @S.Lott Personally, I would use break and return statements as often as possible in my code because it saves me from having to write complex nested if statements. I honestly don't see your argument as valid because you've failed to prove why break statements are hard to follow. I other words, your argument doesn't hold its own. Your statement "For educational purposes—it makes sense to forbid it" sounds like the typical faceless justification I'd expect to hear from a professor with little real-world experience. That's why your answer probably rubs me the wrong way. - Evan Plaice
Adding a break increases the complexity of the code and thus increases the chance of a bug. However, rarely will other ways of coding it be less complex. - Loren Pechtel
(2) I'd like to say, for this question, I think this is a SUPERB ANSWER because most engineering real-world answers should be qualified. The need to prove that something is robust with pre and post conditions is not an academic exercise and can be an essential part of some domains. I'm not an academic, I've been developing software almost all of my 25+ year career and I've worked on a lot of legacy code as well as having ongoing involvement in how to improve software development as a profession. For the record, I believe in careful use of breaks inside loops, with a very clear comment! - Andy Dent
@S.Lott as for your statement "Every statement (especially every composite statement) has a post-condition. If you can't articulate this post-condition, you can't really say much about the program." Assumes that the loop logic only contains one conditional statement. It's perfectly acceptable to have a loop with multiple chainlinked conditionals 'while(true && sky == 'blue' || world == notEnding)'. In this case, a loop exiting can mean many things. The 'break' statement is nothing but a keyword that allows you to de-couple logical conditionals from the loop definition. - Evan Plaice
If you don't like this answer, you can always improve -- post-conditions can be evil to handle with breaks -- superb answer +1! - hhh
@Evan Plaice: "Assumes that the loop logic only contains one conditional statement" Not it doesn't. "multiple chainlinked conditionals" is acceptable. There's still a single post-condition. And break can sometimes make it murky rather than clear. "de-couple logical conditionals from the loop definition" is the problem -- the post-condition really exists (the post-condition is the meaning or semantics of the loop). But the expression is scattered around in various places inside the loop syntax. - S.Lott
@S.Lott I never said it didn't. I was just pointing out that the way you presented your argument is only valid for statements that only contain one conditional. If it contains multiple conditionals then you can't easily articulate what the post-condition is anyway. Who cares if it's scattered because 'break' statements still respect scope. Any proficient/capable programmer should have no issues with reading between the brackets. IE, your argument doesn't hold any weight. You say, "reading the code is hard," I say, "suck less at programming." I say tomat-o you say tom-ah-to. ;) - Evan Plaice
@Evan Plaice: A loop has one post-condition by definition. The statement after the loop has a condition which must be true. Therefore, the loop has one -- possibly complex -- but still singular condition. The argument is based on this definition. "Who cares if it's scattered" is my point. Some people find it hard to read when it's scattered. And it's hard to formalize a proof that the expected post-condition (which must exist, by definition) is actually achieved. The code might be "clear" to some, but a tidy proof that it always works can be made harder with bad break statements. - S.Lott
@S.Lott Why don't try reading the whole statement before quoting and rebuking next time. I said that the scattering doesn't matter because breaks respect scope. If you don't understand scope or can't wrap your head around the contents of the code block, either the code is crap to begin with or you aren't a very talented programmer because it's a pretty basic skill. Excessively wrapping loops with assertion proofs sounds a bit excessive (and useless) anyway. If the loop is so complex that it requires an assertion it probably needs to be re-factored. - Evan Plaice
@Evan Plaice: 'don't use break statements rule' I don't believe I said that. I believe I only said they are a potential bug. "Programming is a professional skill". Correct. Simplicity and clarity are signs of professionalism. Please don't read more into the answer than break is a potential bug because it obscures the post-condition. That's all. You're free to use all the break statements you want. I'm free to view them as potential bugs until proven otherwise. - S.Lott
(1) @S.Lott (cont) Which leads me to my main point. Programming is a professional skill so why is canon like the 'don't use break statements rule' so commonly preached. I get that goto statements were the bane of a lot of programmers existence ::cough::BASIC::/cough:: but it's 2011 and those days are long behind us. Is this type of stuff preached in schools or just universally common practice among programmers because I see it a lot. Disclaimer: I initially learned programming by writing code not sitting through lectures - Evan Plaice
In well designed languages, including modern C, loop control variables are local in scope to the loop and thus they cannot be tested in a post condition without exporting them to outer scope variables. The correct post condition for X is <undefined>. People who don't grasp this are incompetent at both theory and practice. - Jim Balter
@Evan "::cough::BASIC::/cough::" -- actually it was assembler language and FORTRAN II that Dijkstra was dealing with when he wrote his "Goto considered harmful" piece. And these "break is bad" theorists don't understand his point and don't understand how radically different break is from goto. Someone said that they were taught not to use breaks because they aren't structured programming but that's just SO WRONG ... break is a structured replacement for goto. - Jim Balter
(1) foreach(Thing stuff in things) { if(stuff.Id == thingImLookingFor) { // do stuff break; } } -- here, stuff is scoped locally to the loop. This is good code. var thing; while (not IsThingImLookingFor(x)) next x; do stuff -- here, stuff got turned into thing, x, and stuff. Whichever one we settle on for the loop control variable, it has to be global in scope relative to the loop. This is awful code. - Jim Balter
44