In Kotlin, what does a bare `return` statement inside a lambda passed to `forEach` do, and how is that different from returning from the lambda alone?
answer
- Bare return = leave the whole fun (non-local)
- return@forEach = skip one element (like continue)
- Non-local return needs inline function
- No bare break for forEach
- Label is named after the called function
basics
~10 sA bare return inside a forEach lambda exits the whole surrounding function, not just the loop step. To skip only the current step you write return@forEach, which leaves the lambda and continues the loop.
solid answer
~40 sA plain `return` in a lambda is a non-local return: it returns from the nearest enclosing named function (e.g. the method that calls `forEach`), not from the lambda. This works only because `forEach` is an inline function, so the lambda body is copied into the caller and a real `return` is legal there. If you want to exit just the lambda for the current element, use a qualified return `return@forEach`, which acts like `continue` in a normal loop. The label after `@` is the implicit label Kotlin assigns from the function name the lambda is passed to. With a non-inline higher-order function a bare `return` won't compile, forcing the qualified form.
code
kotlin · 9 linesfun firstEven(nums: List<Int>): Int? {
nums.forEach {
if (it % 2 != 0) return@forEach // skip odds, keep looping
return it // non-local: return from firstEven
}
return null
}
println(firstEven(listOf(1, 3, 4, 6))) // 4go deeper
Knows bare return leaves the function and return@forEach skips one element.
Explains the inline requirement and that there is no break for forEach.
Discusses how inlining generates the actual return and when the bare form is rejected.
Reasons about API design implications — choosing inline vs non-inline to permit or forbid non-local returns, and readability trade-offs.
## The two kinds of `return` in a lambda Kotlin distinguishes between a **non-local return** and a **local (qualified) return**. - A **bare `return`** inside a lambda is a *non-local return*: it returns from the nearest enclosing **named function** (a `fun`), skipping out of the lambda and everything around it. - A **qualified return** `return@label` returns only from the lambda labelled `label`, leaving the surrounding function running. ```kotlin fun hasNegative(numbers: List<Int>): Boolean { numbers.forEach { if (it < 0) return true // non-local: returns from hasNegative } return false } ``` Here `return true` exits `hasNegative` entirely the moment a negative is seen. ## Why this is even allowed: `inline` `forEach` is declared `inline`. The compiler copies the lambda body straight into the call site, so a `return` there compiles to a real return of the enclosing function. **Non-local returns are only possible from lambdas passed to `inline` functions.** If `forEach` were a normal (non-inline) function, the bare `return` would be a compile error and you'd be forced to qualify it. ## Qualified return — the `continue` analogue To skip just the current element (like `continue` in a `for` loop), qualify the return with the **implicit label**, which Kotlin names after the function the lambda is passed to: ```kotlin fun printPositives(numbers: List<Int>) { numbers.forEach { if (it <= 0) return@forEach // skip this element, keep looping println(it) } } ``` There is no built-in `break` equivalent for `forEach`; to stop early you either use a non-local `return`, switch to a real `for` loop with a labeled `break`, or use `takeWhile`/`firstOrNull`. ## Key terms - **Lambda**: an anonymous function literal like `{ ... }`. - **Non-local return**: a `return` that jumps out past the lambda to a named function. - **`inline`**: a function modifier that pastes the function (and its lambda) into the caller, enabling non-local returns. - **Implicit label**: the auto-generated `@forEach` / `@functionName` label you target with `return@`.
- Why does a bare `return` compile inside `forEach` but not inside a lambda passed to a custom non-inline function?Because `forEach` is `inline`, so the lambda body is inlined into the caller and a real return of the enclosing function is generated. Non-inline lambdas are compiled to separate objects, where a non-local return has no target, so it's a compile error.
- How do you stop iterating early over a list without a real `for` loop?Use a non-local `return` from an enclosing function, or use functions like `firstOrNull`, `find`, `takeWhile`, or `any`/`all` that short-circuit — `forEach` itself has no `break`.
Bare return is leaving the building; return@forEach is just stepping out of the current room and back into the hallway.
saying these in an interview costs you the question
- Thinking a bare `return` only exits the lambda or the loop
- Claiming `return@forEach` exits the surrounding function
- Saying `break` works directly inside a `forEach` lambda
- Not knowing non-local return requires an inline function
- Confusing the @label position (writing `@forEach return`)