skip to content

Explain the difference between returns(), returns(true), returns(false), and returnsNotNull() in a Kotlin contract. Give a stdlib or realistic example where each is the right choice.

level: middleimportance: should knowfreq 45%

answer

  1. returns() = any normal return (throwers)
  2. returns(true)/(false) = Boolean predicates
  3. returnsNotNull() = non-null result implies fact
  4. isNullOrEmpty uses returns(false)
  5. implies condition limited to is/!= null on params

basics

~10 s

They describe which return outcomes guarantee a condition. returns() means 'any normal return'; returns(true)/returns(false) tie the guarantee to a specific Boolean result; returnsNotNull() ties it to returning a non-null value.

solid answer

~40 s

All four are returns effects inside contract { }. `returns()` (no argument) applies when the function returns normally regardless of value — used by require/check, which throw on failure, so any return means the condition held. `returns(true)` and `returns(false)` are used by Boolean-returning predicates: e.g. a validate(): Boolean can declare returns(true) implies (input != null), so the compiler smart-casts only inside the true branch. `isNullOrEmpty()` in stdlib uses returns(false) implies (this@isNullOrEmpty != null), so after `if (!s.isNullOrEmpty())` s is non-null. `returnsNotNull()` applies when the function's own return value is non-null; pair it with implies to narrow an argument, e.g. returnsNotNull() implies (value != null). The right choice depends on how callers branch on the result: unconditional (require) vs Boolean (predicate) vs nullable result.

code

kotlin · 9 lines
kotlin
@OptIn(ExperimentalContracts::class)
fun isValidName(x: Any?): Boolean {
    contract { returns(true) implies (x is String) }
    return x is String && x.isNotBlank()
}

fun greet(n: Any?) {
    if (isValidName(n)) println("Hi, ${'$'}{n.uppercase()}")  // n is String
}

go deeper

for a junior

Recognizes the four modes exist and that require uses returns().

for a middle

Correctly maps each mode to a use case and writes a returns(true) predicate contract.

for a senior

Explains the restriction that conditions reference only parameters/receiver and chooses returns(false) for isNullOrEmpty deliberately.

for a principal

Reasons about API design: which mode gives callers the cleanest smart-cast ergonomics and the soundness risk of an unchecked contract.

## Returns effects, precisely A returns effect describes a **return mode** of the function and the **condition** that the compiler may then assume. Syntax: `<returnMode> implies <booleanExpression>`. The four return modes: ### 1. `returns()` — normal return, any value Used when the function **throws** on the bad path, so *any* normal completion proves the condition. ```kotlin public inline fun check(value: Boolean) { contract { returns() implies value } if (!value) throw IllegalStateException("Check failed.") } ``` After `check(x is String)`, `x` is `String` because the only alternative was an exception. ### 2. `returns(true)` — returns the Boolean literal true Used by predicates that *return* a Boolean instead of throwing. The condition is assumed **only in the branch where the result is true**. ```kotlin fun isLongString(x: Any?): Boolean { contract { returns(true) implies (x is String) } return x is String && x.length > 3 } if (isLongString(v)) { println(v.length) } // v smart-cast to String here ``` ### 3. `returns(false)` — returns the Boolean literal false Mirror image. The stdlib's `isNullOrEmpty` uses it: ```kotlin public inline fun CharSequence?.isNullOrEmpty(): Boolean { contract { returns(false) implies (this@isNullOrEmpty != null) } return this == null || this.length == 0 } if (!s.isNullOrEmpty()) { println(s.length) } // s is non-null ``` Returning `false` means the receiver was non-null and non-empty, so non-nullness is implied on the false path. ### 4. `returnsNotNull()` — the function returns a non-null value Applies to the function's **own return value being non-null**, which can imply something about an argument or receiver. ```kotlin fun firstOrThrowSpec(list: List<String>?): String { contract { returnsNotNull() implies (list != null) } return list!!.first() } ``` ## Choosing the right mode - Function **throws** on failure → `returns() implies (...)`. - Function **returns Boolean** and you branch on it → `returns(true)`/`returns(false)`. - Function **returns a nullable** and a non-null result tells you something → `returnsNotNull()`. ## Constraints The condition after `implies` may only be a null check (`x != null`), an `is`/`!is` check, or a logical combination of these on **parameters/receiver** — not arbitrary expressions. The compiler trusts the contract; it does not verify the body matches.

  • Can the implies condition reference a local variable from inside the function?
    No. It must reference the function's parameters or receiver. The compiler reasons about the call site, so only values visible there (arguments) can be narrowed.
  • Why does isNullOrEmpty use returns(false) rather than returns(true)?
    Because the useful guarantee (the receiver is non-null) holds when the function returns false. Returning true means it was null OR empty, which says nothing definite about non-nullness.

saying these in an interview costs you the question

  • Saying returns(true) narrows the type on both branches
  • Confusing returnsNotNull() (about the function's result) with returns() implies (x != null) (about an argument)
  • Claiming you can put any boolean expression after implies
  • Thinking isNullOrEmpty narrows on the true path

context