Local return clarification

Hey guys I’m a bit confused of how local return works in Kotlin. My expectation was that, after running the code below, as soon as the condition is met, it will immediately print End of loop, however the loop will still run for all elements and then print that text. Can some please clarify how does a local return work in this context?. Thanks

fun localReturn(list: List<String>) {
var counter = 1
list.forEach  {
    println(it)
    if (counter == 3) {
        println("counter == 3")
        return@forEach
    }
    counter++
}
println("End of loop")

}

fun main(args: Array<String>) {
localReturn(listOf("1", "2", "3", "4", "5", "6"))
}

In this example, the loop itself lives inside the forEach function, and on every step of the loop it calls into the lambda that you pass to it. The return returns from the lambda, so it interrupts execution of the current loop step, but there is no way for it to influence the execution of the loop itself.

Here’s an equivalent way to rewrite your example without using a lambda:

var counter = 1

fun localReturn(list: List<String>) {
    list.forEach  {
        loopBody(it)
    }
    println("End of loop")
}

fun loopBody(element: String) {
    println(element)
    if (counter == 3) {
        println("counter == 3")
        return
    }
    counter++
}

fun main(args: Array<String>) {
    localReturn(listOf("1", "2", "3", "4", "5", "6"))
}

Thanks for your answer @yole. So, does that mean that a local return inside a foreach cannot stop the execution of the loop.

Yes, exactly. A local return inside any lambda function cannot stop the execution of the function which invokes the lambda.

Got it. Thanks for your help

Local return works fine for me. Code:

    fun main(args: Array<String>) {
      val list = listOf("1", "2", "3", "4", "5", "6")
      var counter = 1
      run {
        list.forEach {
          println(it)
          if (counter == 3) {
            println("counter == 3")
            return@run
          }
          counter++
        }
      }
      println("End of loop")
    }

Output:

1
2
3
counter == 3
End of loop

I need to use the method “run” as a label, as a real label crashes the compiler:

    fun main(args: Array<String>) {
      val list = listOf("1", "2", "3", "4", "5", "6")
      var counter = 1
      label@
      list.forEach {
        println(it)
        if (counter == 3) {
          println("counter == 3")
          return@label
        }
        counter++
      }
      println("End of loop")
    }

Error:Kotlin: [Internal Error] org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: Argument for @NotNull parameter ‘descriptor’ of org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.mapReturnType must not be null
PsiElement: println(it)
if (counter == 3) {
println(“counter == 3”)
return@label
}
counter++
The root cause was thrown at: KotlinTypeMapper.java:-1
at org.jetbrains.kotlin.codegen.ExpressionCodegen.genQualified(ExpressionCodegen.java:325)

hi @yole

the following code gives the required output, only difference is using return instead of return@forEach, can you please explain the reasoning behind this

/**
 * You can edit, run, and share this code.
 * play.kotlinlang.org
 */
fun main() {
    println("Hello, world!!!")
    localReturn(listOf(1,2,3,4,5))
}

fun localReturn(list: List<Int>) {
var counter = 1
list.forEach  {
    println(it)
    if (counter == 3) {
        println("counter == 3")
        return
    }
    counter++
}
println("End of loop")
}

The difference is return vs return@forEach. forEach code is inlined into your localReturn function, so when you put the return keyword, it returns from the localReturn function, rather than the forEach function.

1 Like