1. Intro

It’s not possible to use the break and continue keywords to jump out of functional loops in Kotlin – at least, not in the traditional way.

For example, we can’t stop the execution of a forEach functional loop by simply writing the break statement.

However, there are techniques we can use to mimic that behavior. Let’s look at some of them.

2. Using Labels

As we know, we can use labels to qualify return statements. We can use this to simulate the traditional ‘continue’ and ‘break’ behavior in functional loops.

2.1. Simulating continue

Let’s look at an example:

val list = listOf(3, 4, 3, 4, 3)
var sum = 0
list.forEach loop@{ number ->
    if (number % 2 == 0) { // skip all even numbers
        return@loop
    }
    sum += number
}
assertTrue { sum == 9 }

Here, we label the forEach loop as ‘loop’. Later, the execution jumps to this label to simulate the ‘continue’ behavior.

We need our functional loop to skip the execution of a particular iteration. To do that, we use the ‘loop’ label with the return@loop statement.

2.2. Simulating break

Similarly, we can also simulate ‘break’ using labels and return statements:

run outer@{
    list.forEach inner@{ number ->
        if (number % 2 == 0) { // 'break' at the first even number
            return@outer
        }
        sum += number
    }
}
assertTrue { sum == 3 }

Unlike the previous example, we need some extra boilerplate to simulate ‘break’. We need to surround the loop within another scope. This is needed so we can return to the outer scope later.

We assign distinct labels to the outer and inner scopes. This is followed by returning to the outer scope from the loop in the inner scope. This is how we simulate the ‘break’ behavior.

To clarify, we create the outer scope because returning to the (inner) loop label, we only skip the current iteration. We don’t stop the execution of the loop itself.

We could also use a return statement with no label. In that case, we’d end up returning the function itself. This may lead to skipping other statements in the function after our loop, hence the need for an extra outer scope.

Notably, we can also use other scope functions here instead of run.

3. Using filter and takeWhile

Another way to imitate the ‘continue’ and ‘break’ behavior is by chaining them with functions like filter and takeWhile.

3.1. Simulating continue With filter

Using continue in a regular for loop, we can skip iterating over the elements we don’t want to process. We can do this with functional loops by using filter:

val list = listOf(3, 4, 3, 4, 
var sum = 0
list.filter { it % 2 != 0 } // skip all even numbers
    .forEach { number ->
        sum += number
    }
assertTrue { sum == 9 }

Here, we filter the elements we want to skip beforehand by using a filter operation. We then iterate over the filtered list. This effectively achieves the same result as using continue in a regular for loop.

3.2. Simulating break With takeWhile

Likewise, we can mimic the break behavior in a functional loop by calling takeWhile before it:

list.takeWhile { it % 2 != 0 } // 'break' at the first even number
    .forEach { number ->
        sum += number
    }
assertTrue { sum == 3 }

As we know, takeWhile filters the list, removing all elements after reaching the first element that doesn’t satisfy the given predicate. By iterating over the resultant list, we achieve the same results as break.

4. Conclusion

Functional loops in Kotlin don’t support break and continue jumps. However, we can use some techniques to imitate this.

It appears that labeled return statements are more efficient. This is due to the overhead of function chaining, which may lead to the creation of intermediate collections.

On the other hand, using filter and takeWhile seems to require the least boilerplate.

As always, the code samples are available over on GitHub.