skip to content

What does `return@map` do, and how does it differ from a bare `return` inside a lambda passed to map?

level: middleimportance: must knowfreq 70%

answer

  1. Bare return = non-local, exits the fun
  2. return@map = local, exits the lambda
  3. Implicit label = function name
  4. return@forEach behaves like continue
  5. Non-local return needs an inline function

basics

~10 s

return@map exits just the current lambda call and supplies that element's result, like 'continue' for the iteration. A bare return would try to exit the whole surrounding function instead.

solid answer

~40 s

Inside a lambda, a **bare `return` is a non-local return**: it returns from the nearest enclosing *named function*, not from the lambda. To return from the lambda itself you use a **qualified return** with an implicit label named after the function the lambda is passed to: `return@map value`. For each invocation of the `map` lambda, `return@map x` makes `x` that element's mapped result and skips the rest of the lambda body. This is essential when you have early-exit logic in a multi-statement lambda. You can also label a lambda explicitly: `list.map loop@ { return@loop x }`. Bare `return` is only legal inside lambdas of **inline** functions (like `map`, `forEach`, `let`); for non-inline lambdas it is a compile error, forcing the qualified form.

code

kotlin · 5 lines
kotlin
val out = listOf(1, 2, 3, 4).map { n ->
    if (n % 2 == 0) return@map -1
    n * n
}
// out == [1, -1, 9, -1]

go deeper

for a junior

Knows return@map exits the lambda and bare return is different.

for a middle

Explains non-local vs local return and that the label defaults to the function name.

for a senior

Ties legality of bare return to inline functions and explains continue-like semantics plus explicit labels for nesting.

for a principal

Reasons about why non-inline lambdas forbid non-local returns (lifetime/escape), and weighs readability of early-return lambdas vs functional alternatives.

## Two kinds of return inside a lambda ### 1. Bare `return` = non-local return A `return` with no label returns from the **nearest enclosing `fun`**, jumping out past the lambda entirely. This only compiles when the lambda is an argument to an **inline** function (the lambda body is inlined into the caller, so the JVM `return` is meaningful): ```kotlin fun findFirstEven(nums: List<Int>): Int? { nums.forEach { n -> if (n % 2 == 0) return n // returns from findFirstEven, not from forEach } return null } ``` ### 2. Qualified `return@label` = local return `return@map` returns from the **lambda invocation itself**. The implicit label is the name of the function the lambda is passed to (`map`, `filter`, `let`, `run`, `forEach`...). It supplies the value for the current element and continues iteration: ```kotlin val result = listOf(1, 2, 3, 4).map { n -> if (n % 2 == 0) return@map 0 // even -> 0, keep going val squared = n * n squared // odd -> n*n } // result == [1, 0, 9, 0] ``` ## Explicit labels You can name a lambda yourself, which is required if the implicit name is ambiguous or you nest lambdas: ```kotlin listOf(1, 2, 3).forEach outer@ { listOf(10, 20).forEach { inner -> if (inner == 20) return@outer // skip rest of OUTER lambda for this element } } ``` ## `return@map` vs continue/break - `return@forEach` behaves like **`continue`** — it ends the current iteration's lambda body and moves on. - There is no direct lambda equivalent of `break`; use a real loop, `takeWhile`, `first { }`, or a non-local `return`. ## Inline matters For a **non-inline** functional parameter, a bare `return` is illegal (the lambda may outlive the function), so you must use a qualified `return@label`. The standard collection operators (`map`, `filter`, `forEach`) and scope functions (`let`, `run`, `apply`, `also`, `with`) are all `inline`, so both forms are available there.

  • Why can't you use a bare return in a lambda passed to a non-inline higher-order function?
    The lambda could be stored and called after the enclosing function returns, so a non-local return would be meaningless. The compiler rejects it; you must use return@label.
  • How would you simulate break inside a forEach?
    Use a non-local return from the enclosing function, or restructure with first { }, takeWhile, or an indexed/explicit loop.

Bare return is the building's fire exit (you leave the whole building); return@map is just stepping out of the current room and back into the hallway.

saying these in an interview costs you the question

  • Claiming bare return exits only the lambda
  • Not knowing the label defaults to the function name
  • Thinking return@map works in any function regardless of inline
  • Confusing return@forEach (continue) with break
  • Using bare return to mean 'skip this element'

context