Explain the signatures of run and with, including how the lambda's receiver type works and what inline/contracts imply for control flow and capture.
answer
- T.() -> R = function literal with receiver; this is T
- both are inline -> no lambda allocation
- bare return = non-local; return@run for local
- callsInPlace EXACTLY_ONCE contract
- contract enables val init + smart-cast across the call
basics
~20 srun is an extension that takes a lambda where the object is the receiver; with is a top-level function taking the object plus that lambda. Both are inline, so the block runs in place, allowing non-local returns and avoiding extra objects.
solid answer
~40 srun is fun <T, R> T.run(block: T.() -> R): R and with is fun <T, R> with(receiver: T, block: T.() -> R): R. The block type T.() -> R is a function literal with receiver, so inside it this is the T object and the body can call its members unqualified. Both are inline functions, meaning the compiler substitutes the lambda body at the call site: no Function object is allocated, captured variables aren't boxed, and a bare return inside the block is a non-local return from the enclosing function (use return@run / return@with for a local one). The standard-library declarations carry callsInPlace(block, EXACTLY_ONCE) contracts, so the compiler knows the block runs exactly once, enabling definite-assignment of vals set inside and smart-casts to flow through.
code
kotlin · 13 lines// non-local return from inlined block
fun firstHost(uri: java.net.URI?): String {
uri?.run {
if (host == null) return "unknown" // returns from firstHost
return host // also non-local
}
return "none"
}
// EXACTLY_ONCE contract lets this compile:
val token: String
with("abc") { token = uppercase() }
println(token) // definitely assignedgo deeper
Recognizes the block exposes this and run/with return the result, without the inline/contract detail.
Understands the function-literal-with-receiver and basic labeled returns.
Explains inline implications (no allocation, non-local return) and the EXACTLY_ONCE contract enabling val init and smart-casts.
Reasons about performance in hot paths, control-flow pitfalls of non-local returns, and how contracts shape compiler guarantees in library design.
## Exact signatures ```kotlin public inline fun <T, R> T.run(block: T.() -> R): R public inline fun <R> run(block: () -> R): R // receiver-less overload public inline fun <T, R> with(receiver: T, block: T.() -> R): R ``` - `run` (extension): `T` is the receiver type; `block` is a **function literal with receiver** `T.() -> R`. - `with`: takes the object as the first **argument** and the same receiver-typed block. - There's also a receiver-less `run(block: () -> R)` for scoping a plain block. ## Function literal with receiver `T.() -> R` means inside `block`, **`this` is a `T`**. That's why `s.run { length }` works — `length` resolves against the `this` receiver `s`. The receiver also shadows outer `this`; use a **labeled this** (`this@OuterClass`) to reach an enclosing receiver. ## inline: what it buys Both are marked **`inline`**. The compiler copies the lambda body directly into the call site instead of creating a lambda object. Consequences: - **No allocation / no boxing** of captured variables — zero runtime overhead vs hand-written code. - **Non-local return**: a bare `return` inside the block returns from the **enclosing function**, because the body is inlined there. ```kotlin fun find(u: User?): String { u?.run { if (banned) return "blocked" } // returns from find return "ok" } ``` To return from the lambda only, use the **label**: `return@run value` or `return@with value`. ## Contracts: callsInPlace The stdlib declares these with a **contract**: ```kotlin contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } ``` This tells the compiler the block is invoked **exactly once**, enabling: - **Definite assignment**: a `val` assigned only inside the block is considered initialized afterward. - **Smart-cast** facts established in the block to be used after it. ```kotlin val x: Int run { x = 42 } // legal: contract guarantees the block ran once println(x) ``` ## Why it matters at senior level - Knowing they're `inline` justifies using them in hot paths without worrying about lambda allocation. - Knowing non-local return semantics avoids surprises where a `return` escapes the block. - Knowing the `EXACTLY_ONCE` contract explains why the compiler accepts `val` initialization and flow facts across the call. ## with vs run at the bytecode level After inlining, `with(x) { body }` and `x.run { body }` compile to essentially the same code; the choice is stylistic and (for `run`) nullability-driven.
- What's the difference between return and return@run inside x.run { }?return exits the enclosing function (non-local, allowed because run is inline); return@run only exits the run block and uses its argument as the block result.
- Why does the compiler accept assigning a val inside run { }?The stdlib contract callsInPlace(block, EXACTLY_ONCE) proves the block executes exactly once, satisfying definite-assignment analysis.
saying these in an interview costs you the question
- Not knowing run/with are inline
- Thinking a bare return inside run exits only the lambda
- Being unaware of the EXACTLY_ONCE contract / definite assignment
- Confusing the receiver type T.() -> R with a plain (T) -> R parameter