Inside a crossinline lambda, which kinds of return statements are legal and which are rejected, and why?
answer
- return@label = local = allowed
- bare return = non-local = rejected
- Reason: caller frame may be gone
- Implicit label is the function's name
- Loops' break/continue inside lambda still fine
basics
~20 sA labeled local return like return@functionName is allowed — it exits just the lambda. A bare non-local return that would exit the calling function is rejected, because the lambda may run in a context where the caller is no longer on the stack.
solid answer
~40 sInside any lambda, a bare 'return' inside an inline function's plain lambda is a non-local return that exits the enclosing function. crossinline removes exactly that capability. So inside a crossinline lambda: local returns are legal — 'return@label' (the implicit label is the inline function's name, e.g. return@onClick), which exits only the lambda; and you can still use loops with labeled break/continue scoped to the lambda. What is rejected is the bare non-local 'return' targeting the outer function, because the crossinline lambda may be invoked indirectly (inside a Runnable or nested lambda) where unwinding the caller's frame is impossible. The compiler enforces this at the call site, producing an error like 'return is not allowed here'. This is purely about control flow; the lambda is still inlined.
code
kotlin · 10 linesinline fun onClick(crossinline handler: () -> Unit) {
view.setOnClickListener { handler() }
}
fun wire() {
onClick {
// return // compile error: non-local return forbidden
return@onClick // OK: exits only this lambda
}
}go deeper
Knows a bare return is blocked but return@label still works inside the lambda.
Distinguishes local vs non-local return clearly and names the implicit label convention.
Explains the stack-frame rationale for why crossinline must forbid non-local returns.
Connects the rule to control-flow safety guarantees and reasons about how it shapes the design of inline callback DSLs.
## Two kinds of return in lambdas - **Non-local return**: a bare `return` written inside a lambda that returns from the **enclosing function**. Only possible for inline-function lambdas (plain, not crossinline/noinline), because the body is copied into the caller. - **Local return**: `return@label` that exits **only the lambda**. The implicit label is the name of the function taking the lambda. ```kotlin inline fun each(xs: List<Int>, f: (Int) -> Unit) { for (x in xs) f(x) } fun demo(xs: List<Int>) { each(xs) { if (it < 0) return // non-local: returns from demo() if (it == 0) return@each // local: skips to next iteration's call } } ``` ## What crossinline forbids `crossinline` removes the **non-local** return only. Inside a crossinline lambda: - `return@functionName` (local return) — **allowed**. Exits just the lambda. - bare `return` (non-local) — **rejected** with a compile error such as *"'return' is not allowed here"*. - labeled `break`/`continue` for loops written inside the lambda — allowed (they're local to that lambda's own loops). ```kotlin inline fun onClick(crossinline handler: () -> Unit) { view.setOnClickListener { handler() } } onClick { // return // ERROR: non-local return forbidden by crossinline return@onClick // OK: local return } ``` ## Why the restriction exists A non-local return must unwind the caller's stack frame. A crossinline lambda is, by definition, invoked in **another execution context** — e.g. inside a `Runnable`, a nested lambda, or a callback object — possibly after the caller has already returned. There is no valid frame to return to, so the language forbids it outright rather than risk undefined behavior. ## Contrast quickly - Plain inline lambda: both local and non-local returns allowed. - crossinline lambda: only local returns. - noinline lambda: only local returns too (it's a normal function value, so non-local return was never possible). ## Keywords non-local `return`, `return@label`, `inline`, `crossinline`, labeled `break`/`continue`, call-site inlining.
- What is the implicit label name for a local return inside an onClick { } lambda?return@onClick — the implicit label is the name of the function that accepts the lambda.
- Why can't the compiler just make non-local returns work for crossinline lambdas?Because the lambda runs in another context where the caller's stack frame may already be gone, so there is nothing to return to.
saying these in an interview costs you the question
- Saying all returns are forbidden inside a crossinline lambda
- Claiming return@label is illegal under crossinline
- Not knowing the implicit label is the function name
- Asserting non-local returns are merely discouraged, not blocked
- Confusing the rule with noinline's behavior (where non-local return was never possible anyway)