skip to content

Write a custom function with a returns-effect contract so callers get a smart cast, and list the rules/limitations the contract block must obey (placement, opt-in, what conditions are allowed).

level: middleimportance: should knowfreq 38%

answer

  1. contract{} must be first statement
  2. @OptIn(ExperimentalContracts::class)
  3. Conditions: is / != null on params only
  4. Compiler trusts, does not verify body
  5. Returns effects don't need inline

basics

~20 s

Put a contract { } block as the very first statement, opt in with the experimental annotation, and use returns(...) implies (param is Type / param != null). Then callers can use the parameter as the narrowed type after the call.

solid answer

~40 s

Declare the contract as the first statement of the function body, before any other code. Opt in with @OptIn(ExperimentalContracts::class) or @ExperimentalContracts since the API is still experimental. Inside, call contract { } and write effects like returns() implies (x != null) or returns(true) implies (x is String). Limitations: the condition may only be an is/!is check or a null comparison on the function's parameters or receiver (not locals, not arbitrary expressions); you cannot combine returns effects with logic the compiler cannot model; the compiler does not verify your body actually honors the contract, so a wrong contract is unsound. Functions are commonly inline but returns effects do not require it (callsInPlace does). After the call, the parameter is smart-cast only if it is a stable value (val or unmodified local var).

code

kotlin · 8 lines
kotlin
@OptIn(ExperimentalContracts::class)
fun Any?.isNonEmptyText(): Boolean {
    contract { returns(true) implies (this@isNonEmptyText is String) }
    return this is String && this.isNotEmpty()
}

val x: Any? = "hi"
if (x.isNonEmptyText()) println(x.length)  // x: String

go deeper

for a junior

Can copy a require-style contract and knows it needs an opt-in.

for a middle

Writes a correct contract from scratch with proper placement and a valid implies condition.

for a senior

Articulates the soundness gap and stability requirements, and that returns effects don't need inline.

for a principal

Weighs the experimental status and unverified-trust model when deciding to expose contract-bearing APIs in a shared library.

## A working example ```kotlin import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract @OptIn(ExperimentalContracts::class) fun requireString(value: Any?): String { contract { returns() implies (value is String) } if (value !is String) throw IllegalArgumentException("not a String") return value } fun use(v: Any?) { requireString(v) println(v.length) // v smart-cast to String } ``` ## The rules ### Placement The `contract { }` call must be the **first statement** in the function body. Anything before it (even a `val`) is a compile error: *"Contract should be the first statement."* ### Opt-in The contracts API lives in `kotlin.contracts` and is **experimental**. You must opt in: ```kotlin @OptIn(ExperimentalContracts::class) ``` or annotate the declaration with `@ExperimentalContracts` and propagate it. (The marker has existed since Kotlin 1.3 and is still experimental in 2.x.) ### Allowed conditions after `implies` Only: - a null check on a parameter/receiver: `x != null`, `x == null` - a type check: `x is Type`, `x !is Type` - boolean combinations (`&&`, `||`, `!`) of the above The operands must be the function's **parameters or receiver** — never locals declared inside the function and never arbitrary calls. You also cannot reference a value the call site can't see. ### Soundness is on you The compiler **does not check** that the body actually enforces the contract. If you write `returns() implies (value is String)` but never validate it, you create an unsound smart cast that can throw `ClassCastException` at runtime. Treat contracts as a trusted assertion. ### inline? Returns effects do **not** require `inline`. (Only `callsInPlace` effects on a lambda parameter need `inline` to be useful.) Many stdlib helpers are inline for other reasons. ### Stability of the smart cast Even with a correct contract, the smart cast only applies to **stable** targets: a `val`, or a local `var` not captured/modified by a closure. A mutable property with a custom getter or a captured var will not smart-cast. ## Common error messages - *"Contract should be the first statement"* — move it to the top. - *"Contracts are not allowed for..."* — e.g. on local functions, anonymous functions, or where unsupported. - *"This declaration is experimental..."* — add the opt-in.

  • What happens if your contract claims something the body never enforces?
    The compiler trusts it and emits a smart cast with no runtime check, so a later use can throw ClassCastException. Contracts are unverified, so an incorrect one is genuinely unsafe.
  • Why must the contract be the first statement?
    It is a declarative compile-time annotation, not runtime logic. The compiler requires it in a fixed position so it can extract it before analyzing the body; the runtime call is essentially a no-op.

saying these in an interview costs you the question

  • Putting other statements before contract { }
  • Forgetting the experimental opt-in
  • Referencing local variables in the implies condition
  • Believing the compiler validates the body against the contract
  • Claiming returns effects require inline

context