skip to content

How does a bare 'return' behave inside an anonymous function versus inside a lambda passed to forEach? Explain local vs non-local return.

level: middleimportance: must knowfreq 60%

answer

  1. anon fun return = local
  2. lambda bare return = non-local (outer fn)
  3. non-local return needs inline (forEach)
  4. return@forEach = continue equivalent
  5. anon fun = explicit local return, no label

basics

~20 s

In an anonymous function, 'return' exits just that function. In a lambda, a bare 'return' exits the whole enclosing function (a non-local return). That different behavior is the main reason to pick an anonymous function.

solid answer

~50 s

A bare return inside an anonymous function is a **local return**: it returns from the anonymous function only, like return in any normal fun. A bare return inside a lambda is a **non-local return**: it returns from the nearest enclosing function that declared the lambda, not from the lambda itself. This works only when the lambda is passed to an inline function (such as forEach, which is inline) — a non-inline lambda cannot contain a bare return at all and won't compile. To return only from a lambda you must use a label: return@forEach. So given a loop over a list, return inside a forEach lambda aborts the whole outer function, whereas the same logic written as forEach(fun(item) { return }) only skips to the next element. Choosing an anonymous function makes the 'return from this block' intent explicit without a label.

code

kotlin · 6 lines
kotlin
fun demo(): String {
    listOf(1, 2, 3).forEach { if (it == 2) return "non-local: left demo()" }
    listOf(1, 2, 3).forEach(fun(x: Int) { if (x == 2) return /* local: just this element */ })
    return "reached end"
}
// demo() returns "non-local: left demo()" because the first forEach short-circuits.

go deeper

for a junior

Can state that 'return' exits the anonymous function but exits more in a lambda, even if fuzzy on details.

for a middle

Clearly explains local vs non-local and that non-local return requires an inline function.

for a senior

Explains the inlining mechanism, labels, and picks the right form to express short-circuit vs skip intent.

for a principal

Reasons about how this affects control-flow readability and refactoring safety across a codebase and sets guidelines.

## The core distinction: local vs non-local return A **local return** returns control to the immediate enclosing function literal. A **non-local return** returns control out of an *outer* function — past the function literal's boundary. ### Inside an anonymous function: always local ```kotlin fun findFirstEven(nums: List<Int>): Int? { nums.forEach(fun(n: Int) { if (n % 2 == 0) return // returns from the anonymous fun only -> like 'continue' }) return null } ``` Here `return` exits only the anonymous function for that one element. The loop continues. So this version can **never** short-circuit `findFirstEven`; it always reaches `return null`. ### Inside a lambda: bare return is non-local ```kotlin fun findFirstEven(nums: List<Int>): Int? { nums.forEach { n -> if (n % 2 == 0) return n // returns from findFirstEven, NOT from the lambda } return null } ``` A bare `return n` here exits `findFirstEven` entirely with `n`. ## Why the lambda can do this at all: inlining `forEach` is declared `inline`. The compiler copies the lambda body into the call site, so a `return` in that body is physically inside `findFirstEven` and can legally return from it. A bare non-local `return` is **only permitted in lambdas passed to inline functions**. Pass a lambda with a bare `return` to a non-inline function and it won't compile. ## Returning only from a lambda: labels To get local-return behavior from a lambda you add a **label**: ```kotlin nums.forEach { n -> if (n % 2 == 0) return@forEach // skip to next element, like 'continue' } ``` `return@forEach` (or a custom `loop@` label) is the lambda equivalent of the anonymous function's plain `return`. ## Summary table - Anonymous function + `return` -> local (this block only), always allowed. - Lambda + `return` -> non-local (outer function), only inside inline functions. - Lambda + `return@label` -> local, always allowed. This is the single biggest *behavioral* reason to choose an anonymous function over a lambda.

  • Why can't you write a bare 'return' in a lambda passed to a non-inline function?
    Without inlining the lambda becomes a separate Function object, so its body isn't part of the enclosing function — a non-local return would have no frame to return from. The compiler rejects it.
  • What is the lambda equivalent of an anonymous function's plain return?
    A labeled return such as return@forEach, which returns only from the lambda for the current iteration.

A lambda's bare return is an emergency exit out of the whole building; an anonymous function's return just leaves the room.

saying these in an interview costs you the question

  • Saying 'return' behaves the same in both forms
  • Believing any lambda can contain a bare non-local return regardless of inline
  • Confusing return@forEach with returning from the outer function
  • Not knowing forEach is inline and why that matters
  • Calling the anonymous function's return 'non-local'

context