Compare `noinline` and `crossinline`: what does each control, and when do you pick one over the other?
answer
- noinline: become an object, give up inlining
- crossinline: stay inlined, ban non-local return
- crossinline for nested/indirect call contexts
- noinline to store/return/pass onward
- Mutually exclusive on one parameter
basics
~20 sBoth apply to a lambda parameter of an inline function. noinline un-inlines the lambda so it becomes a real object you can store or pass on. crossinline keeps it inlined but bans return that would exit the caller, which is needed when the lambda runs from another context.
solid answer
~40 sA plain inlined lambda is (a) not an object and (b) allowed to do a non-local `return` from the enclosing function. The two modifiers change exactly one of those properties each. `noinline` makes the lambda a real `FunctionN` object (giving up inlining) so it can be stored/returned/forwarded; non-local returns then no longer apply because it isn't inlined. `crossinline` keeps the lambda inlined (still no object) but forbids non-local returns, which is mandatory when the lambda is invoked from a different execution context — e.g. inside another lambda, a Runnable, or a local object/class — where a non-local `return` would be unsound. So: need it as a value => `noinline`; still inline it but call it from a nested/indirect context => `crossinline`.
code
kotlin · 7 linesinline fun demo(
crossinline nested: () -> Unit, // inlined, no non-local return
noinline stored: () -> Unit // real object, can be saved
): () -> Unit {
Runnable { nested() }.run()
return stored
}go deeper
Knows both are inline-function lambda modifiers and that noinline makes an object.
States the object-vs-non-local-return distinction and gives a correct use case for each.
Explains the soundness reason crossinline exists (nested context + stack frame) and why the two are mutually exclusive.
Reasons about API ergonomics — when a crossinline DSL beats exposing noinline-stored callbacks, and the codegen/allocation implications of each choice.
## Two independent properties of an inlined lambda A default inlined lambda parameter has two characteristics: 1. **No object** — its body is spliced in at call sites. 2. **Non-local return allowed** — `return` inside the lambda returns from the *enclosing* function (because the body is textually inside it). Each modifier flips exactly one: | Modifier | Object created? | Non-local return allowed? | Use when | |---|---|---|---| | (none) | No (inlined) | Yes | You only call the lambda directly | | `noinline` | **Yes** | No (it's a real object) | You store / return / pass it to a non-inline fn | | `crossinline` | No (still inlined) | **No (banned)** | You inline it but call it from a nested context (another lambda, Runnable, local class/object) | ## Why crossinline exists If an inlined lambda were invoked from a different context — say inside a `Runnable` you pass to a thread — a non-local `return` would try to return from a function whose frame may no longer be on the stack. That's unsound, so Kotlin requires `crossinline`, which keeps inlining but **disallows** the non-local `return` at compile time. ```kotlin inline fun onClick(crossinline action: () -> Unit) { val listener = Runnable { action() } // nested context: needs crossinline register(listener) } ``` ## Why noinline exists If you need the lambda as a value, inlining is impossible — so `noinline` un-inlines it. ```kotlin inline fun keep(noinline action: () -> Unit): () -> Unit = action ``` ## Picking one - Need the lambda as a **value** (store/return/pass to non-inline)? => `noinline`. - Want to keep it **inlined** but call it from a **nested/indirect** context? => `crossinline`. - Only **call it directly**? Use neither. They are mutually exclusive on the same parameter: `noinline` already gives up inlining, so `crossinline`'s non-local-return restriction is irrelevant.
- Can a single parameter be both `noinline` and `crossinline`?No. They are mutually exclusive; `noinline` already un-inlines the lambda, making crossinline's non-local-return constraint moot.
- Does `crossinline` allocate a Function object?No. The lambda is still inlined; crossinline only removes the non-local-return capability. noinline is the one that creates an object.
noinline = take the worker off the assembly line so you can ship them elsewhere; crossinline = keep them on the line but forbid them from pulling the emergency stop for the whole factory.
saying these in an interview costs you the question
- Saying crossinline creates an object (it doesn't)
- Saying noinline bans non-local returns as its primary purpose
- Thinking they can be combined on one parameter
- Confusing 'nested context' with 'storing the lambda'