You're designing a higher-order API. How do you decide between making a function `inline` with a `noinline` parameter versus just writing a plain function?
answer
- Inline only if a lambda benefits (hot / return / reified)
- noinline the lambda you must store
- All-noinline => write a plain function
- Inline duplicates bytecode per call site
- Library hot paths justify inline; app code rarely does
basics
~20 sMake it inline only if at least one lambda benefits from inlining (hot calls, non-local returns, or reified types). Use noinline for the one lambda you must keep as an object. If every lambda needs to be an object, just write a normal function.
solid answer
~40 sInlining pays off when a lambda is called frequently (avoiding allocation/dispatch), or when you need a feature only inline gives you: non-local returns from the lambda, `reified` type parameters, or zero-overhead DSL helpers. If at least one parameter benefits, mark the function `inline` and tag the lambda(s) you need as values `noinline`. But inlining duplicates the body's bytecode at every call site, so it's wrong for large bodies or rarely-hot paths. The decisive question: 'Does any lambda benefit from being inlined?' If yes => inline + noinline the stored ones. If every lambda must be an object (none benefits), there's nothing to inline — drop `inline` and write a plain function, avoiding the redundant-noinline warning and bytecode bloat. Library hot-paths (collection ops, `use`, `synchronized`) justify inline; ordinary application code usually doesn't.
code
kotlin · 5 lines// Mixed: `body` benefits from inlining, `cleanup` is stored.
inline fun guarded(body: () -> Unit, noinline cleanup: () -> Unit) {
registry.add(cleanup) // stored -> noinline
body() // hot -> inlined
}go deeper
Knows inline is for performance and noinline keeps a lambda as an object.
Lists concrete inline benefits (hot calls, reified, non-local return) and uses noinline for stored lambdas.
Articulates the full tradeoff including bytecode duplication and the all-noinline 'just use a plain function' rule.
Sets a team/library policy: where inlining is justified, binary-compatibility implications of public inline APIs, and instruction-cache/codegen costs at scale.
## The decision framework Start from what `inline` actually buys you, then check whether any parameter needs it. ### Reasons to make a function `inline` 1. **Hot lambda** — called often; inlining removes per-call `FunctionN` allocation and virtual `invoke` (e.g. `map`, `filter`, `forEach`). 2. **Non-local returns** — you want `return` inside the lambda to exit the caller (only inlined lambdas allow this). 3. **`reified` type parameters** — only inline functions can have them (`inline fun <reified T> ...`). 4. **Low-overhead control-flow helpers / DSLs** — `run`, `let`, `with`, `use`, `synchronized` are inline to be free. ### Cost of `inline` - The function body is **duplicated** into every call site => larger bytecode, possible instruction-cache pressure for big bodies. - Public inline functions leak implementation into call sites (binary-compatibility care). ## Where `noinline` fits If you've decided to inline (because at least one lambda benefits), but one lambda must be **stored / returned / passed to a non-inline function**, tag exactly that parameter `noinline`. You keep the benefit for the others and pay one object for the stored one. ```kotlin // Good: inline pays off for `action` (hot, non-local return); // `onError` is stored, so it's noinline. inline fun retrying( action: () -> Unit, noinline onError: (Throwable) -> Unit ) { try { action() } catch (e: Throwable) { errorHandlers.add(onError); throw e } } ``` ## When to NOT inline If **every** lambda parameter would be `noinline` — i.e. none is called in a way that benefits — then inlining buys nothing but bytecode bloat, and the compiler warns that the `inline` is effectively pointless. Write a **plain function** instead: ```kotlin // All lambdas are just stored -> no inline benefit -> plain function fun register(onStart: () -> Unit, onStop: () -> Unit) { starts.add(onStart); stops.add(onStop) } ``` ## Rule of thumb - Some lambda benefits from inlining => `inline`, with `noinline` on the value-typed ones. - No lambda benefits => plain function. - Reserve aggressive inlining for small, hot, library-grade helpers — not every app-level higher-order function.
- What does the Kotlin compiler tell you if an inline function gains no benefit from inlining?It warns that the function's `inline` modifier has no real effect (e.g. when all lambdas are noinline / nothing is inlined), suggesting you drop `inline`.
- Why are stdlib functions like `let` and `use` inline?They are tiny, ubiquitous control-flow helpers; inlining makes them zero-overhead and lets blocks use non-local returns and (for some) reified types.
saying these in an interview costs you the question
- Marking everything inline reflexively for 'speed'
- Inlining large function bodies, bloating bytecode
- Keeping inline when every lambda is noinline
- Ignoring binary-compatibility risks of public inline funcs
- Not citing reified/non-local-return as inline-only motivations