Why can you assign to a val exactly once inside a `run { }` lambda and have the compiler accept it as definitely initialized afterwards? What language feature makes this work?
answer
- run/let/with carry EXACTLY_ONCE contracts
- callsInPlace = invoked in place + how many times
- enables definite-assignment of val in lambda
- without contract: lambda assumed 0..many times
- stdlib declares it; consumer needs no opt-in
basics
~20 sThe standard-library functions like run and let promise the compiler that the lambda you pass runs exactly once. Because of that promise, the compiler knows a val you set inside the lambda gets set once, so it treats it as initialized.
solid answer
~40 sScope functions such as `run`, `let`, `with`, and `apply` carry a Kotlin *contract* declared with the `contracts` block and `callsInPlace(block, InvocationKind.EXACTLY_ONCE)`. This tells the compiler the lambda parameter is invoked in place, synchronously, exactly once before the function returns. With that guarantee the compiler's definite-assignment analysis can prove a `val` written once inside the lambda is assigned exactly once — so it is initialized after the call and can be referenced, and the `val` cannot be re-assigned. Without the contract the compiler would see the lambda as possibly never called (or called many times) and reject `val` assignment inside it, forcing you to use a different pattern. The contract is in the stdlib source, so you get this for free using these functions.
code
kotlin · 6 linesval name: String
run {
val raw = readLine().orEmpty()
name = raw.trim() // assigning a val from inside the lambda — allowed
}
println(name) // definitely initialized herego deeper
Knows that you can set a val inside run/let and use it afterwards, and that it's something special these functions provide.
Names the contract and EXACTLY_ONCE, and explains definite-assignment analysis as the mechanism.
Can reproduce the stdlib contract declaration and explain why a custom function without it fails; distinguishes 'in place' from invocation count.
Frames it as how compile-time effects extend the type system's flow analysis, and discusses the experimental status and stability trade-offs of authoring such contracts.
## The problem A `val` (read-only local) in Kotlin must be assigned **exactly once** along every path. The compiler runs *definite-assignment analysis* to prove this. When you pass a lambda to a normal higher-order function, the compiler cannot know whether or how many times that lambda runs, so it conservatively assumes it might run **zero or many** times. Writing a `val` inside such a lambda would then look like "maybe never assigned" or "maybe assigned twice" — both illegal. ```kotlin fun myOwn(block: () -> Unit) { block() } val x: Int // without a contract, this fails myOwn { x = 42 } // error: captured val cannot be assigned / not initialized println(x) // error: variable might not be initialized ``` ## The fix: callsInPlace Kotlin **contracts** let a function describe its behavior to the compiler. The relevant effect is `callsInPlace`: ```kotlin import kotlin.contracts.InvocationKind import kotlin.contracts.contract @OptIn(ExperimentalContracts::class) inline fun <R> myRun(block: () -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block() } ``` `callsInPlace(block, kind)` promises two things: - **In place**: the lambda is invoked *synchronously*, before the enclosing function returns — not stored, not run later on another thread. - **Kind**: how many times — here `EXACTLY_ONCE`. With `EXACTLY_ONCE`, the compiler now reasons: the lambda runs once, so a `val` assigned inside runs once. Definite-assignment passes: ```kotlin val x: Int myRun { x = 42 } // OK: assigned exactly once println(x) // OK: definitely initialized ``` ## The real stdlib functions The standard scope functions already declare this. Their source contains, for example: ```kotlin public inline fun <R> run(block: () -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block() } ``` So `run`, `let`, `with`, `also`, `apply`, and `repeat` (which uses `AT_LEAST_ONCE`) all benefit from it. That's why this pattern works without you writing any contract yourself. ## Key terms - **Contract**: compile-time-only metadata describing a function's effect; defined in a `contract { }` block as the first statement of the body. - **Definite assignment**: the compiler proof that a `val`/`var` is set before use. - **InvocationKind**: enum — `EXACTLY_ONCE`, `AT_MOST_ONCE`, `AT_LEAST_ONCE`, `UNKNOWN`. Note contracts are still marked experimental (`@ExperimentalContracts`) for *authoring*, but consuming the stdlib's contracts requires no opt-in.
- Would the same val assignment compile if you wrote your own `fun apply2(block: () -> Unit)` without a contract?No. Without `callsInPlace`, the compiler assumes the lambda may run zero or many times, so it rejects the val assignment and treats the variable as possibly uninitialized.
- Does this require the function to be `inline`?The stdlib functions are inline, but `callsInPlace` itself works for non-inline functions too; it just describes invocation, independent of inlining.
It's a signed promise note: the function swears 'I'll call your lambda exactly once, right now' so the compiler trusts a one-time val assignment inside it.
saying these in an interview costs you the question
- Says it works 'because run is inline' (inlining is unrelated to the assignment proof)
- Thinks any higher-order function allows val assignment inside its lambda
- Confuses this with smart casts / `returns` effects
- Believes the lambda result is what makes it work, not the contract
- Claims you must add @ExperimentalContracts to just call run