Local return clarification


#1

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"))
}

#2

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"))
}

#3

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


#4

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


#5

Got it. Thanks for your help


#6

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)