7 minutes read

Have you ever had a situation when you wanted to have more control over loop iterations and wished you could jump out of a loop manually? For example, when a special condition occurs or when you need to process only certain cases and skip others? Fortunately, Kotlin has a special tool for it: structural jump expressions. There are three structural jump expressions in Kotlin: break, continue, and return. What makes them even more flexible is labels. Let's take a look at these features now.

The Break Statement

A break expression is used to terminate the nearest enclosing loop. You can literally break the loop with your own condition. In the example below, we used it in the if-else case:

for (i in 1..10) {  
   // do something
   if (checkCondition){  
       break 
   }  
}

The for loop terminates when the if condition executes the break expression. Here is a more specific example:

println("Before the loop")  
for (i in 1..10) {  
    if (i == 3) {  
        break  
    }  
    println(i)  
}  
println("After the loop")  

In the third iteration, the break expression will leave the for loop. The output is:

Before the loop  
1
2
After the loop

As you can see, nothing happens with the code before and after the loop – break just terminates the nearest enclosing loop.

The Continue Statement

Let’s have a look at the next keyword, continue. It lets us proceed to the next iteration of the enclosing loop. In other words, in case you want to skip the current iteration of the loop code with your own condition, you may use continue. For instance:

var result = ""
for (i in "hello world") {
    if (i == 'o') continue
    result += i
}
println(result)

This code will produce the following output:

hell wrld

You should use continue when you need to ignore one iteration. But be careful – code like this is really hard to read.

Inner loops and structural jump expressions

Oftentimes, you may have one loop within another. Let's see how break and continue work in such a case.

Look at the following example:

for (i in 1..4) {
    for (j in 1..4) {
        if (j == 2) continue // you want to ignore j = 2
        if (i <= j) break    // you will print the string if i is greater than j
        println("i = $i, j = $j")
    }
    println("Finished to examine i = $i")
}

The result is:

Finished to examine i = 1
i = 2, j = 1
Finished to examine i = 2
i = 3, j = 1
Finished to examine i = 3
i = 4, j = 1
i = 4, j = 3
Finished to examine i = 4

As you can see, break and continue terminate only one loop. But what should we do if we need to terminate the outer loop? In this case, you should use labels. Let' take a look at them.

Labels

What is a label? Technically, it's just an identifier with the @ sign at the end, for example: loop@, kotlin@. You can use almost any word with it, apart from the reserved words in Kotlin. What do you need labels for? Well, you can add your label to a loop:

loop@ for (i in 1..10) {
    // do something
}

Now, let's combine labels with the jump expressions described above and see what happens.

Here is an example of using the break statement with a label:

loop@ for (i in 1..3) { 
    for (j in 1..3) {
        println("i = $i, j = $j")   
        if (j == 3) break@loop  
    }  
}  

Both the inner and outer for loops are terminated at the third iteration. The output is this:

i = 1, j = 1
i = 1, j = 2
i = 1, j = 3

So, labels help us break not only the inner loop but also the outer one. Once again, break with a label terminates the execution of the labeled loop.

Now, let's modify the previous example and use the continue keyword:

loop@ for (i in 1..3) {
    for (j in 1..3) {
        for (k in 1..3) {
            if (k == 2) continue@loop
            println("i = $i, j = $j, k = $k")
        }
    }
}

The output result is as follows:

i = 1, j = 1, k = 1
i = 2, j = 1, k = 1
i = 3, j = 1, k = 1

With the label, we changed the default behavior of the continue keyword. Instead of skipping the execution of the inner loop at the second iteration and continuing with the next iteration, we returned to the outer loop and continued its execution. Without labels, such a trick wouldn't be possible.

When and structural jump expressions

Let's take a look at another useful feature of Kotlin 1.4 or newer. Now you can use break and continue inside when without labels! See how it works.

Before Kotlin 1.4, you needed to write something like this:

Loop@ for (i in 1..10) {
    when (i) {
        3 -> continue@Loop
        6 -> break@Loop
        else -> println(i)
    }
}

It is easy to understand that in this example, you skip the iteration when i equals 3 and break the loop when it equals 6. The result is:

1
2
4
5

Now, let's rewrite this example!

for (i in 1..10) {
    when (i) {
        3 -> continue
        6 -> break
        else -> println(i)
    }
}

This code does the same thing but looks more clear!

The Return Statement

Without a label, the return statement lets us return the result to the nearest enclosing function. It can be really helpful if we want to jump out of a loop for some reason and skip the remaining loop code or exit the current function:

fun foo() {
    val number = intArrayOf(1, 2, 3, 4, 5)
    for (it in number) {
        if (it == 3) return // non-local return directly to the caller of foo()
        print(it)
    }
    println("this point is unreachable")
}

fun main() { 
    foo() // calling foo()
    println()
    println("foo() is over")
    for (i in 1..10) {
        for (j in 1..10) {
            println("i = $i, j = $j")
            if (j == 3) return // local return to the caller of main()
        }
        println("this point is unreachable")
    }
    println("this point is unreachable")
}

The output result is:

12
foo() is over
i = 1, j = 1
i = 1, j = 2
i = 1, j = 3

In the example above, the internal loop will be interrupted at the third iteration and return the flow of program execution. This way, the outer loop will never reach further than the first iteration.

You can use labels with the return statement, too, but it's a more complicated case and we will discuss it later. The idea is the same, though: you will just jump to the label.

Conclusion

Let's summarize how to use structural jumps.

The break statement exits the loop immediately and skips the remaining iterations. The main difference between break and continue is that continue will just skip the rest of the current cycle but will proceed to the following iterations.

Finally, with labels, you can exit the outer/inner loop and continue your processing from the place you need.

All the considered statements can also be used with the while loop.

The return statement immediately returns the result of the function, and the code after the return keyword will not be executed.

Remember, it's hard to read and fix code with a lot of structural jump expressions. Be very careful and do not overuse them!

570 learners liked this piece of theory. 13 didn't like it. What about you?
Report a typo