Compare `return` behavior in a local function versus in a (non-inline and inline) lambda. What surprises candidates here?
answer
- local fun: return is always local
- non-inline lambda: bare return illegal, use return@label
- inline lambda: bare return = non-local, exits outer fn
- crossinline blocks non-local return
- no bare-return shortcut out of a local fun
basics
~20 sIn a local function, a plain return just exits that local function. In a lambda, a plain return is only allowed when the lambda is inlined, and then it exits the whole outer function. That difference trips people up.
solid answer
~40 sA local function is a real named function, so a bare `return` returns from the local function itself — never from the enclosing function. A lambda is different: a non-inline lambda cannot use a bare `return` at all (it would be a non-local return, which is illegal there); you must use a labeled return like `return@forEach`. When the lambda is passed to an `inline` function, a bare `return` is allowed and performs a non-local return from the enclosing function, because the lambda body is inlined into it. The common surprise: people expect `return` inside a local function to break out of the outer function like it sometimes does in inline lambdas, but it doesn't. Local functions give predictable, local control flow; for non-local exit you'd throw or restructure.
code
kotlin · 8 linesfun demo(list: List<Int>): String {
fun check(x: Int): Boolean {
if (x < 0) return false // returns from check only
return true
}
list.forEach { if (it > 100) return "big" } // inline forEach -> exits demo
return if (list.all { check(it) }) "ok" else "bad"
}go deeper
Knows return exits 'the function'; may not distinguish local function from lambda.
Clearly separates local-function return from inline vs non-inline lambda return rules.
Explains non-local returns, labeled returns, and crossinline precisely.
Designs APIs/control flow choosing local functions vs inline lambdas for clear, safe exit semantics.
## Local function: `return` is always local A local function is a genuine named function with its own frame. A bare `return` exits **the local function**, full stop: ```kotlin fun process(items: List<Int>) { fun handle(x: Int) { if (x < 0) return // returns from handle, NOT process println(x) } items.forEach { handle(it) } } ``` There is no way for `handle`'s plain `return` to exit `process`. ## Lambdas: it depends on inlining Lambdas behave differently because of **non-local returns**: - **Non-inline lambda:** a bare `return` is a compile error. You must use a **labeled return** scoped to the lambda: ```kotlin list.forEach { if (it == 0) return@forEach } // skip this element only ``` - **Inline-function lambda:** because the lambda body is **inlined** into the caller, a bare `return` is allowed and performs a **non-local return** from the **enclosing function**: ```kotlin fun find(list: List<Int>): Int? { list.forEach { if (it > 0) return it } // forEach is inline -> returns from find return null } ``` `forEach` is declared `inline`, so `return it` exits `find`. ## The surprise Candidates often assume `return` inside a local function can break out of the outer function (mirroring the inline-lambda behavior). It cannot — local functions always return locally. Conversely, they may think a non-inline lambda allows bare `return`; it doesn't. ## How to get non-local exit - From a local function: there's no bare-return shortcut. Options are returning a sentinel/`Boolean` and letting the caller act, throwing an exception, or restructuring the loop. - From an inline lambda: a bare `return` already exits the enclosing function; use `return@label` to stay local. ## Quick reference - Local function + `return` -> exits local function. - Non-inline lambda + bare `return` -> compile error; use `return@lambda`. - Inline lambda + bare `return` -> exits enclosing function (non-local return); `crossinline` forbids it.
- Why can an inline lambda use a bare `return` to exit the outer function but a non-inline one cannot?The inline lambda's body is copied into the caller, so `return` targets the enclosing function. A non-inline lambda is a separate function object, so a non-local return would be unsafe and is disallowed.
- What does `crossinline` do to this?It marks an inline-function lambda parameter as not allowed to perform non-local returns, typically because it's invoked from another execution context.
saying these in an interview costs you the question
- Saying `return` in a local function exits the enclosing function
- Claiming any lambda allows a bare `return`
- Not knowing labeled returns (return@forEach) exist
- Confusing inline vs non-inline lambda return rules
- Thinking local functions support non-local returns