skip to content

Where does the label in `return@let` or `return@forEach` come from, and when must you declare an explicit label instead?

level: middleimportance: should knowfreq 52%

answer

  1. Implicit label = called function's name
  2. label@ { } before the brace for explicit
  3. Nested same-name lambdas -> need explicit
  4. Anonymous fun uses plain return, no @
  5. return@forEach hits the innermost forEach

basics

~20 s

The label after @ is automatically the name of the function the lambda was given to, like let or forEach. You write your own label when the auto name is ambiguous or you have nested lambdas of the same function.

solid answer

~40 s

Kotlin gives every lambda passed to a function an **implicit label equal to that function's name**, so a lambda passed to `let` can use `return@let`, one passed to `forEach` can use `return@forEach`, and so on. You must switch to an **explicit label** (`name@ { ... }`) when: (1) two **nested lambdas use the same function** (e.g. nested `forEach`) and you need to target the outer one; or (2) you assign the lambda to a variable or pass an **anonymous function** rather than a lambda — anonymous functions use a plain `return` that already returns from the function itself, so no label is needed. Explicit labels are declared as `label@ { ... }` immediately before the opening brace and referenced via `return@label`.

code

kotlin · 6 lines
kotlin
grid.forEach row@ { line ->
    line.forEach { ch ->
        if (ch == '#') return@row   // skip rest of this line
        print(ch)
    }
}

go deeper

for a junior

Knows the implicit label is the function name like @let or @forEach.

for a middle

Can declare explicit labels and knows nested same-name lambdas require them.

for a senior

Contrasts lambdas vs anonymous functions for return semantics and chooses the clearer construct.

for a principal

Establishes team conventions for early-return readability (explicit labels vs anonymous fun vs extraction) and reasons about maintainability.

## Implicit labels When you write `foo { ... }`, the lambda automatically gets a label named **`foo`** (the called function's name). So: ```kotlin value?.let { if (it.isBlank()) return@let // exits the let lambda, let returns Unit here process(it) } items.forEach { if (it.skip) return@forEach // continue to next item handle(it) } ``` The implicit label is **only the simple function name** — `return@map`, `return@filter`, `return@run`, `return@apply`, `return@also`, `return@with`. ## When the implicit label is ambiguous With **nested same-named** functions, `return@forEach` targets the **innermost** one. To target the outer, declare an explicit label: ```kotlin rows.forEach rows@ { row -> row.cells.forEach { cell -> if (cell.isEmpty()) return@rows // skip the rest of THIS row render(cell) } } ``` An explicit label is written `name@` directly before the lambda's `{`. You may also override the implicit name even without nesting for clarity: `list.map mapper@ { ... return@mapper x }`. ## Anonymous functions don't need labels An **anonymous function** (`fun(x: Int): Int { ... }`) is a different construct: a plain `return` inside it returns from that anonymous function itself (local), so you never write `return@`: ```kotlin listOf(1, 2, 3).map(fun(n: Int): Int { if (n == 2) return 0 // returns from this anonymous fun, like return@map return n * n }) ``` This is a common alternative when early returns make a lambda awkward. ## Summary of forms - `return` (bare) — non-local, exits enclosing `fun`; needs inline. - `return@functionName value` — implicit label, exits the lambda. - `return@customLabel value` — explicit label declared with `customLabel@`. - `return` inside an **anonymous function** — local to that function, no `@`.

  • If you have nested forEach and write return@forEach, which loop does it affect?
    The innermost forEach lambda. To exit the outer one early you must give it an explicit label and use return@thatLabel.
  • Why does an anonymous function not need an @label for local return?
    Its plain return already returns from the anonymous function itself, which is exactly the local-return behavior you want.

saying these in an interview costs you the question

  • Thinking you can pick any label name without declaring it
  • Believing return@forEach targets the outer of nested loops
  • Using return@let when the lambda was passed to run
  • Adding @labels to anonymous functions
  • Not knowing explicit labels go before the opening brace

context