skip to content

Anonymous Functions

The fun(x: Int): Int { ... } literal spells out parameter and return types and, crucially, an unlabeled return exits that function rather than the enclosing one. That difference from lambdas is exactly why the form still exists.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

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

open as a page

What is an anonymous function in Kotlin, and how does its syntax differ from a lambda expression?

level: juniorimportance: should knowfreq 45%

basics

~20 s

An anonymous function is a function without a name, written with the fun keyword like fun(x: Int): Int { return x * 2 }. Unlike a lambda, you write parameter and return types explicitly and use a normal return.

open as a page

When does an anonymous function's block body return Unit unexpectedly, and how do explicit vs inferred return types behave for block versus expression bodies?

level: middleimportance: should knowfreq 35%

basics

~20 s

An anonymous function with a block body { } returns Unit unless you declare a return type or use a return statement. An expression body (= ...) infers its type from the expression. So a block body without an explicit type can surprise you by returning Unit.

open as a page

How do you declare a receiver type on an anonymous function, and how does an anonymous function help with overload resolution where a lambda would be ambiguous?

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

You can write fun Foo.(x: Int): Int { ... } to give an anonymous function a receiver, producing a function-with-receiver type. Because anonymous functions state parameter and return types explicitly, they can resolve overloads that a bare lambda leaves ambiguous.

open as a page

As a tech lead, what guidelines would you give for choosing an anonymous function over a lambda, and what are the maintainability and performance implications?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

Prefer concise lambdas by default. Use an anonymous function when you need a plain local return without a label, an explicit return type, a stated receiver, or to resolve overload ambiguity. Both compile to the same function types, so there is no inherent performance gap.

open as a page