How do you declare a function that never returns, and what compiler behaviors does a Nothing return type unlock (TODO, error, require)?
answer
- Return type Nothing => every path throws/loops/exits
- Unlocks unreachable-code + smart-cast
- TODO(), error(), exitProcess() return Nothing
- require/check use kotlin.contracts, not Nothing
- returns() implies / returnsNotNull()
basics
~20 sGive the function the return type Nothing and make sure every path throws or loops forever. Then the compiler knows code after a call to it is dead and can smart-cast variables, the way TODO() and error() do.
solid answer
~40 sDeclare `fun fail(msg: String): Nothing = throw ...`. The body must guarantee no normal return — typically a single `throw`, an infinite loop, or `exitProcess()`. Once the return type is `Nothing`, the compiler treats any statement after a call as unreachable (it reports 'Unreachable code'), and it propagates smart-casts because control provably leaves. The stdlib leans on this: `TODO()`, `error()`, and the body of `requireNotNull`/`checkNotNull`'s failure path all return `Nothing`. Contracts also matter: `require(cond)`, `check(cond)`, `requireNotNull(x)` are declared with `@kotlin.contracts.ExperimentalContracts`-backed contracts (returns/returnsNotNull) so that after the call the compiler smart-casts (e.g. `x` becomes non-null). The Nothing return type is what makes the 'fall-through is impossible' guarantee available to flow analysis.
code
kotlin · 6 linesfun fail(message: String): Nothing = throw IllegalStateException(message)
fun lookup(map: Map<String, Int>, key: String): Int {
val v = map[key] ?: fail("missing $key")
return v // v smart-cast to non-null Int
}go deeper
Knows TODO()/error() exist and that they throw; can stub a function with TODO().
Declares : Nothing functions correctly and explains unreachable-code + smart-cast effects.
Distinguishes Nothing-return from contract-backed helpers (require/checkNotNull) and names the contract DSL.
Explains how flow analysis composes Nothing returns with kotlin.contracts to drive narrowing, and the soundness constraints on declaring Nothing.
## Declaring a never-returning function A function never returns normally if **every** execution path ends in a throw, an infinite loop, or process exit. You signal this to the type system by declaring the return type as `Nothing`: ```kotlin fun fail(message: String): Nothing = throw IllegalStateException(message) fun loopForever(): Nothing { while (true) { /* ... */ } } ``` If you declare `: Nothing` but a path *can* fall through, the compiler rejects it with "A 'return' expression required" / type mismatch, because returning `Unit` is not a `Nothing`. ## What the Nothing return unlocks ### 1. Unreachable-code detection ```kotlin fun handle(x: Int) { fail("stop") println(x) // warning: Unreachable code } ``` The compiler knows control cannot continue past `fail`. ### 2. Smart-cast / type narrowing in callers ```kotlin fun len(s: String?): Int { if (s == null) fail("null!") // Nothing-returning branch return s.length // s smart-cast to String (non-null) } ``` Because the `null` branch never returns, the compiler knows that past it `s` is non-null. ## Standard-library members that return Nothing - **`TODO()`** / `TODO(reason)` — throws `NotImplementedError`; lets stub code typecheck in any return position. - **`error(message)`** — throws `IllegalStateException`. - **`exitProcess(code)`** (in `kotlin.system`) — terminates the JVM. ## Contract-backed helpers (related but not Nothing-returning) `require`, `check`, `requireNotNull`, `checkNotNull` usually **do** return (when the condition holds). They use the `kotlin.contracts` DSL: ```kotlin // conceptual signature public inline fun require(value: Boolean) { contract { returns() implies value } if (!value) throw IllegalArgumentException(...) } ``` - `require(cond)` -> `returns() implies cond`: after the call `cond` is known true (smart-cast). - `requireNotNull(x)` / `checkNotNull(x)` -> `returnsNotNull()` plus the value, so `x` smart-casts to non-null. The **failure** path inside them effectively has `Nothing` type (it throws), which is what lets `requireNotNull(x) ?: ...` and similar flow correctly. So `Nothing` and contracts together drive the smart-casting you feel when using these helpers. ## Key distinction - `Nothing`-return = *never* returns (always throws/loops). - Contract helpers = *conditionally* throw, and use `contract {}` to tell the compiler what holds **if** they return.
- Does require() return Nothing?No. require() returns Unit when the condition holds; only its internal throw path is Nothing. Its smart-cast power comes from a kotlin.contracts `returns() implies` contract, not from a Nothing return type.
- What happens if you annotate a function `: Nothing` but one path falls through?It fails to compile: the fall-through would return Unit, which is not a subtype of Nothing, so you get a type-mismatch / missing-return error.
saying these in an interview costs you the question
- Claiming require()/check() return Nothing
- Saying Nothing-returning functions return null or Unit
- Forgetting that every path must not return
- Not knowing TODO() throws NotImplementedError
- Thinking the smart-cast comes from runtime checks, not the type system