Start learning free

LESSON 156 OF 414 · LOOPING OVER ARRAY INDICES

Break In For Loops in Scala: Stopping a Loop Early

Lesson Read the full explanation

Scala does not have a built-in break statement. In the simple function-based tutorials here, we can stop the loop by returning from the current function.

When Scala reaches the return statement, it exits the function immediately, so the loop stops too.


Breaking a for loop:

In this example, we will terminate the loop when we find a "banana" in our array:

val fruits = Array("kivi", "orange", "banana", "apple", "pear")
for (fruit <- fruits) {
	println(fruit)
	if(fruit == "banana") {
		return
	}
}

The output of this code is:

kivi
orange
banana

As you can see, our program doesn't print apple and pear.

Here's what happens during this loop:

1. First iteration:
	a. fruit = "kivi"
	b. println(fruit); => Output: kivi
	c. Is fruit == "banana"? No.
	
2. Second iteration:
	a. fruit = "orange"
	b. println(fruit); => Output: orange
	c. Is fruit == "banana"? No.
	
3. Third iteration:
	a. fruit = "banana"
	b. println(fruit); => Output: banana
	c. Is fruit == "banana"? Yes:
          return => Exit the function immediately

Here's the same program but iterating through the array with indices instead:

val fruits = Array("kivi", "orange", "banana", "apple", "pear")
for (i <- fruits.indices) {
	println("Index " + i + ": " + fruits(i))
	if(fruits(i) == "banana") {
		return
	}
}

The output of this code is:

Index 0: kivi
Index 1: orange
Index 2: banana

As you can see, our program doesn't print apple and pear.


Assignment
Let's print all the lessons our student finished!


Hint
Look at the examples above if you get stuck.


STEP 1 OF 3
    Ctrl + Enter to check

    SCALA · LOOPING OVER ARRAY INDICES

    Break In For Loops in Scala, explained

    Stopping a loop that has done its job

    Many languages have a break keyword that ends a loop in the middle. Scala doesn't. In the simple function-based exercises of this chapter, you stop a loop by leaving the function that contains it with return. When Scala reaches return, the function ends immediately, and so does any loop inside it.

    An example with a banana

    This function prints fruits until it has printed a banana:

    def printUntilBanana(fruits: Array[String]): Unit = {
      for (fruit <- fruits) {
        println(fruit)
        if (fruit == "banana") {
          return
        }
      }
    }
    
    printUntilBanana(Array("kivi", "orange", "banana", "apple", "pear"))
    
    kivi
    orange
    banana
    

    On the third pass fruit is "banana". The program prints it first, because println comes before the check. Then the if succeeds and return leaves the function. The two remaining fruits, apple and pear, are never visited.

    The order of the two statements decides what is printed. If the check came first, the banana would not be printed. That is a common source of off-by-one results with early exits: decide whether the matching item should be processed before the loop stops or not.

    The same loop with indices

    Looping over indices works the same way. The index lets you print the position as well:

    for (i <- fruits.indices) {
      println("Index " + i + ": " + fruits(i))
      if (fruits(i) == "banana") {
        return
      }
    }
    
    Index 0: kivi
    Index 1: orange
    Index 2: banana
    

    What return does, and what to watch for

    • return leaves the whole function. It stops the loop, but it also skips everything written after the loop. In the banana function the loop is the last thing, so nothing is lost. If you add a line such as println("done") after the loop, it prints when the loop runs out of fruits and doesn't print when return fires.
    • return belongs to a method. In your own programs, put the loop inside a def, as the examples do.
    • A warning from the compiler. A for loop is a method call that takes your loop body as a function, so return inside it leaves the outer method from inside that function. Scala 3.5 compiles this and prints a warning that such returns are no longer supported, pointing to scala.util.boundary. The code still runs and stops the loop as shown above.

    The newer way: boundary

    Scala 3 offers boundary and break for this job. You mark a block with boundary and leave it with break, optionally passing a value out:

    import scala.util.boundary, boundary.break
    
    def indexOfBanana(fruits: Array[String]): Int = boundary {
      for (i <- fruits.indices) {
        if (fruits(i) == "banana") break(i)
      }
      -1
    }
    

    Called with Array("a", "banana", "c") it returns 1, and with Array("a") it returns -1. The lesson uses plain return, which is simpler for a first look at early exits.

    The exercise

    The array lessons holds "variables", "conditionals", "arrays", "functions" and "loops", and lastFinishedLesson is "functions". The task is to print every lesson the student finished, which means everything up to and including the last finished one.

    1. Loop over the lessons. Write for (lesson <- lessons). The arrow reads as "for each lesson in lessons".
    2. Print the lesson. Use println("Finished " + lesson). The + joins the two strings.
    3. Stop at the last finished lesson. Add an if that compares lesson with lastFinishedLesson and does return when they are equal. It goes after the print, so the last finished lesson is printed before the loop stops.
    val lessons = Array("variables", "conditionals", "arrays", "functions", "loops")
    val lastFinishedLesson = "functions"
    
    for (lesson <- lessons) {
        println("Finished " + lesson)
        if (lesson == lastFinishedLesson) {
            return
        }
    }
    
    Finished variables
    Finished conditionals
    Finished arrays
    Finished functions
    

    The text Finished loops never appears, because the loop ends on the fourth pass. Comparing strings with == checks their contents in Scala, so lesson == lastFinishedLesson is true for equal text and not only for the same object.

    Key takeaways

    • Scala has no break statement; return ends the function and with it the loop.
    • Put the check after the work when the matching item should be processed, and before it when it should be skipped.
    • Code after the loop does not run when return fires inside it.
    • Scala 3 prefers scala.util.boundary with break for leaving a loop from inside a function body.
    Keep going with ScalaAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free