12

My Software Engineering teacher just said: "Avoid using continue and break, always make it work without using these". Is there a problem with these instructions? I would say he didn't say something so coherent, but maybe I don't know something about it?

14
  • 16
    Does this answer your question: Are break and continue bad programming practices?
    – Doc Brown
    Dec 7 '21 at 5:55
  • 3
    Did they say to avoid using those instructions, or to never use them?
    – Nat
    Dec 7 '21 at 7:55
  • 4
    As an example of a case where using break obviously makes sense: say you're looping over a collection, looking for an item. Once you've found it, presumably you'd break out of the loop rather than continuing to search.
    – Nat
    Dec 7 '21 at 7:58
  • 4
    Returning early from a loop is often the only reasonable thing to do, which is why these constructs exist. However, often it is much more readable to replace a break with return, i.e. to refactor an inner loop into its own small method. Dec 7 '21 at 8:20
  • 3
    I have had a lot of issues with developers being told complete nonsense by teachers. Do what you need to do to pass but programming teachers are not generally considered authoritative in software. Quite the opposite, really. Caveat emptor.
    – JimmyJames
    Dec 7 '21 at 18:51
41

Some teachers oversimplify on this topic (especially when they only teach, but don't do daily real-world programming any more). Of course, I don't know if that applies to your teachers, but I would not listen to the advice against continue and break too literally.

continue and break can make loops more readable or less readable, depending on how they are used. The real problem are loops with too large inner bodies and many conditions for stopping them or executing only parts. Having multiple continue and break in such a loop is only a symptom for this "disease", but working around those keywords just formally isn't the cure.

If running into such a situation, one could try to avoid the mentioned keywords by using boolean flags and complex if/else blocks instead, but that will not make the code simpler. Quite the opposite - often it will become even uglier.

So what is the cure?

  • Refactor inner parts of large loops into smaller functions. These functions might return some status information which can be used to control the outer calling loop, and it can be perfectly fine to use break or continue controlled by the returned status. If the functions are still complex, decompose them to smaller functions themselves.

  • Avoid processing too many things in one loop. Instead, organize your code to process sets of data, which can lead to a sequence of two or three simpler loops instead of one complex one. So there might be one loop which produces some intermediate array of data, then a second one which takes the output of the first loop, iterates over this data and hands the result set to a third. Often, each of these loops can be put into a function on their own, returning its result set, which is passed as a parameter into the next function.

You will be astonished how much much simpler your code will get when you just apply these two guidelines rigorously.

23
  • 28
    @Martin Cars involved in traffic jams tend to have four wheels. Thus, any car having four wheels is badly designed, since it should be trivial to make one without it in the first place. Dec 7 '21 at 13:21
  • 2
    @Deduplicator it's quite easy to write good code without break and continue. It's not as easy to make good cars without four wheels.
    – Martin
    Dec 7 '21 at 14:57
  • 17
    @Martin I disagree. I know many instances of code where getting rid of break or continue would be at a cost of readability. Sure, if (!item.interesting()) continue; can be trivially replaced by if (item.interesting()) { ... }, but this extra indentation is unnecessary clutter. Sure, you can extract the loop into a separate function to use return, but that means the continuity of code is broken, and it is often a much bigger hit to readability.
    – Frax
    Dec 7 '21 at 16:13
  • 5
    @Frax Many programming languages nowadays allow working with collections (lists, sequences, sets) in a functional way. In Java for example, you use .filter(Item::isInteresting) to get rid of those elements from your stream. C# has similar functions, as do python and various other languages. Embracing a functional style to work with data often leads to more readable code and way fewer continues and breaks.
    – Polygnome
    Dec 7 '21 at 16:22
  • 3
    @Polygnome You make a good point but I've seen people take this way too far and make their code completely arcane in an attempt to use filters and avoid control statements. Even worse, sometimes it results in performance problems or bugs. When your filter condition is simple, it's a great practice. Beyond one or two steps, filters tend to make code worse IMO.
    – JimmyJames
    Dec 7 '21 at 19:01
17

You've been given some incomplete and oversimplified advice. However, that's not necessarily a bad thing on your teacher's part, as it's nearly impossible to explain the finer nuances of software development practices to newcomers straight out of the gate (without teaching at a snail's pace and boring the students to death).

What your teacher is trying to achieve here is that you learn to write iterations the proper way, without immediately starting to reach for the crutches that continue and break are. It's not that you'll never be allowed to use them as a developer, it's that you are being forbidden from using them right now as a student, specifically so that you learn to write iterations the right way.

It's the equivalent of telling a math student that they can't use a calculator, or an apprentice carpenter that they can't use a nail gun. Not because calculators and nail guns are bad, but because they are a shortcut that causes you to not learn how to do math in your head or create proper fitting wood joints.

2
  • 6
    The advice is oversimplified to the point of being wrong. A found variable is often way, way worse than a break.
    – Jan Hudec
    Dec 7 '21 at 16:50
  • 2
    @JanHudec: IF that is the task put before the students, yes. But breaks and continues can very easily be abused to skirt around tasks that specifically try to teach how to write iterations (e.g. a for which needs an atypical condition evaluation). The teacher is not correct about real world development but they may be correct for their specific curriculum and lesson goals.
    – Flater
    Dec 7 '21 at 17:48
4

Good programming requires prioritizing a number of principles. Two of those principles are to avoid needlessly using "break" and, especially, "continue", but those principles should be set aside when they would conflict with other more important principles.

When I was first learning to program in the 1970s, a common paradigm was:

Read a record
While last read was successful
  Process a record
  Read a record
End While

Writing the code in this way will ensure that every iteration of the loop will run to completion, which is a desirable principle to uphold, but violates another principle "Don't repeat yourself" by requiring that the instructions to read a record be included twice in the code. I think it's valuable to be able to give priority to different principles depending upon circumstances, even though I think for most purposes today a better pattern would be:

Begin Loop
  Read a record
  If read failed then
    Exit Loop
  End If
  Process a record
End Loop

Thus, for purposes of an academic exercise, writing the loop with the old pattern is useful, even though in most situations the modern approach would be more appropriate.

3
  • 1
    Even in the 1970s, the DRY principle was known, of course not under that name. However, Pascal was often used for teaching, and old Pascal dialects simply did not have a break, continue, or "exit loop" equivalent. That typically let to code like in your first example, or to code which made use of boolean flags plus an if/else block inside the loop to avoid the repetition.
    – Doc Brown
    Dec 8 '21 at 14:16
  • 1
    @DocBrown: I recall seeing the first pattern taught for FORTRAN, PL/I, and HP-BASIC, none of would have imposed syntactic restrictions on loop structure. I wouldn't be at all surprised, though, if FORTRAN compilers gained the ability to optimize loops that didn't contain any internal branches before they gained the ability to optimize a wider range of constructs. A compiler may be able to recognize if, after optimization, the tail of a loop matches the code preceding it and replace the earlier code with a jump into the tail, more easily than it could take a common piece of source code...
    – supercat
    Dec 8 '21 at 16:28
  • ...and generate separate pieces of machine code for use before the first iteration and at the end of each subsequent iteration.
    – supercat
    Dec 8 '21 at 16:30
2

Very often the advice for a beginner and for an advanced person is different. In your case: If you ask whether you should avoid continue and break, then you should. Until you are advanced enough to make your own decision.

Important: In following the advice, don't replace continue and break with something worse. Both continue and break can be easily replaced with a goto statement (if your instructor didn't mention goto). That would be 100 times worse. Often break inside a loop can be replaced with a return statement - which is say twice as bad. Often continue or break are the natural way to express your intent, and replacing them makes your code worse.

Where it is excellent advice is when you use break or continue as a convoluted solution to your problem when something else is much easier. For example, if I saw this just at the end of a loop:

if (x == 0) continue;
y = x;

instead of the simple

if (x != 0) y = x;

then get rid of the continue.

6
  • 2
    IMHO your example is contrived to show only what you wanted to show. I generally find that when needing something similar, the "y=x" clause is a large block of code rather than a simple statement. So using the non-continue solution forces an extraneous indentation.
    – Peter M
    Dec 7 '21 at 14:06
  • Why is return 'twice as bad'? Not using early returns is highly correlated with bad code in my experience.
    – JimmyJames
    Dec 7 '21 at 18:47
  • Well, yes. break from a loop just before the end of a function, and using an early return instead, has identical effect. The early return is twice as bad as the break.
    – gnasher729
    Dec 7 '21 at 19:03
  • 1
    @gnasher729 You've reasserted that it's twice as bad but I still don't know 'why' you think that. They don't have identical effects either. break causes a loop to terminate. return causes execution to return to the point at which the function/method was called.
    – JimmyJames
    Dec 7 '21 at 19:55
  • 1
    OK but I don't. You still haven't explained why you think return is twice as bad. It's especially puzzling if you think they have identical effects.
    – JimmyJames
    Dec 9 '21 at 15:11
0

One point not already mentioned is that with break, and possibly continue, reasoning formally about/with loop invariants (also on SO) becomes more complicated.

2
  • 1
    What makes you think that? You execute some code, you skip some other code, as appropriate to make an algorithm work correctly. Whether you use break or continue to skip what needs skipping doesn't make it harder to argue about it.
    – gnasher729
    Dec 7 '21 at 19:01
  • 1
    @gnasher729 Perhaps I was too terse. Or perhaps wrong. Added 'formally' to clarify. I was thinking about precondition, loop invariant, postcondition as taught in some courses. The usual method includes "at the end of while(condition) { ... } you have not condition and loop-invariant true". If you jump out of the loop with a break then the loop condition may still hold, so it's more complicated. What do you think? [By the way, if the downvote is yours, thank you for explaining!]
    – Pablo H
    Dec 9 '21 at 17:22

Not the answer you're looking for? Browse other questions tagged or ask your own question.