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.
answer
- returns() = any normal return (throwers)
- returns(true)/(false) = Boolean predicates
- returnsNotNull() = non-null result implies fact
- isNullOrEmpty uses returns(false)
- implies condition limited to is/!= null on params
basics
~10 sThey 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 sAll 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@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
Recognizes the four modes exist and that require uses returns().
Correctly maps each mode to a use case and writes a returns(true) predicate contract.
Explains the restriction that conditions reference only parameters/receiver and chooses returns(false) for isNullOrEmpty deliberately.
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