When guarding a nullable value, what is the difference between using `value?.let { ... }` and `value?.run { ... }`, and when would you pick each?
answer
- let -> it (argument)
- run -> this (receiver)
- both return the lambda result
- let for renaming/transform, run for many member calls
- rename it in nested blocks
basics
~20 sBoth run their block only when value is non-null. let gives you the value as it; run gives it as this, so you call its members directly without a name. Both return the block's result.
solid answer
~40 sBoth `let` and `run` are scope functions that return the lambda result, so both pair well with `?.` and Elvis. The difference is how the non-null receiver is exposed: `let` passes it as the **argument `it`** (you can rename it: `value?.let { v -> ... }`), while `run` makes it the **receiver `this`**, so you call members directly (`value?.run { length + size }`). Use `let` when you want an explicit name, are transforming into a different type, or need to disambiguate multiple nested receivers. Use `run` when you call several members of the receiver and `this.` noise hurts readability. Both smart-cast the receiver to non-nullable inside the block. With `?.`, a null receiver short-circuits the whole call to null in both cases, so you typically append `?: default`.
code
kotlin · 7 linesdata class User(val email: String?)
fun domainLet(u: User): String =
u.email?.let { it.substringAfter('@') } ?: "none"
fun domainRun(u: User): String =
u.email?.run { substringAfter('@') } ?: "none"go deeper
Knows let uses it and run uses this, and that both run only when non-null.
Correctly maps both to 'returns lambda result' and picks let vs run by readability/renaming needs.
Reasons about nested-it shadowing, transform-to-new-type cases, and keeps the choice consistent across a codebase.
Sets team conventions (e.g. prefer let for null-guards for greppability) and weighs receiver-style ambiguity against terseness.
## The two shapes Kotlin's scope functions differ along two axes: **how they expose the context object** (as `it` argument vs. `this` receiver) and **what they return** (the lambda result vs. the context object itself). | Function | Context object | Returns | |----------|----------------|---------| | `let` | `it` (argument) | lambda result | | `run` | `this` (receiver) | lambda result | | `with` | `this` (receiver) | lambda result | | `apply` | `this` (receiver) | the object | | `also` | `it` (argument) | the object | For **null guarding** you almost always want one that returns the lambda result, so it's `let` or `run`. ## let — the value as `it` ```kotlin val len: Int = name?.let { it.trim().length } ?: 0 ``` `it` is the non-null `String`. You can rename it for clarity in nested blocks: `name?.let { trimmedName -> ... }`. ## run — the value as `this` ```kotlin val len: Int = name?.run { trim().length } ?: 0 ``` Here `trim()` and `length` are called on the implicit `this` (the non-null `name`). No name needed. ## Choosing - **`let`** when you want an **explicit name**, are transforming to a **different type**, or have **nested** scope blocks where `it` would be ambiguous (rename it). - **`run`** when you make **several calls** on the same receiver and `this.` is just noise. ## Both smart-cast After `?.`, inside either block the receiver is the **non-nullable** type, so member access is safe without further `?.`. ## Gotcha: nested `it` shadowing ```kotlin outer?.let { o -> inner?.let { i -> use(o, i) } } // rename to avoid two its ``` Naming the parameters prevents the inner `it` from shadowing the outer one.
- Can you rename the parameter in `let`? In `run`?In `let` yes: `x?.let { renamed -> ... }`. In `run` the receiver is `this` and cannot be renamed, though you can refer to it explicitly via `this`.
- Which would you prefer inside another lambda that already uses `it`?`let` with an explicit name (e.g. `x?.let { value -> ... }`) to avoid shadowing the outer `it`, or `run` so the inner receiver is `this` and doesn't collide.
let hands you the tool with a label on it; run puts the tool directly in your hand so you just start using it.
saying these in an interview costs you the question
- Saying run returns the object (that's apply)
- Claiming let exposes this instead of it
- Not mentioning both short-circuit to null with ?.
- Thinking only let works with null safety
- Ignoring it-shadowing in nested blocks