loopsforeachlambdakotlin

`break` and `continue` in `forEach` in Kotlin


Kotlin has very nice iterating functions, like forEach or repeat, but I am not able to make the break and continue operators work with them (both local and non-local):

repeat(5) {
    break
}

(1..5).forEach {
    continue@forEach
}

The goal is to mimic usual loops with the functional syntax as close as it might be. It was definitely possible in some older versions of Kotlin, but I struggle to reproduce the syntax.

The problem might be a bug with labels (M12), but I think that the first example should work anyway.

It seems to me that I've read somewhere about a special trick/annotation, but I could not find any reference on the subject. Might look like the following:

public inline fun repeat(times: Int, @loop body: (Int) -> Unit) {
    for (index in 0..times - 1) {
        body(index)
    }
}

Solution

  • Edit:
    According to Kotlin's documentation, it is possible to simulate continue using annotations.

    fun foo() {
        listOf(1, 2, 3, 4, 5).forEach {
            if (it == 3) return@forEach // local return to the caller of the lambda, i.e. the forEach loop
            print(it)
        }
        print(" done with explicit label")
    }
    

    If you want to simulate a break, just add a run block

    fun foo() {
        run {
            listOf(1, 2, 3, 4, 5).forEach {
                if (it == 3) return@run // local return to the caller of the lambda, i.e. the forEach loop
                print(it)
            }
            print(" done with explicit label")
        }
    }
    

    Original Answer:
    Since you supply a (Int) -> Unit, you can't break from it, since the compiler do not know that it is used in a loop.

    You have few options:

    Use a regular for loop:

    for (index in 0 until times) {
        // your code here
    }
    

    If the loop is the last code in the method
    you can use return to get out of the method (or return value if it is not unit method).

    Use a method
    Create a custom repeat method method that returns Boolean for continuing.

    public inline fun repeatUntil(times: Int, body: (Int) -> Boolean) {
        for (index in 0 until times) {
            if (!body(index)) break
        }
    }