What does `value?.let { ... }` do, and what is `it` inside the block?
answer
- Block runs only on non-null
- `it` = the non-null value (smart-cast)
- `?.` short-circuits to null, lambda skipped
- Returns the lambda result (it's an expression)
- Best for nullable `var`/properties
basics
~10 sIt runs the block only when value is not null. Inside the block, it is that same value, now guaranteed non-null, so you can use it safely without extra null checks.
solid answer
~40 s`?.let { }` combines the safe-call operator `?.` with the standard-library scope function `let`. If the receiver is `null`, the whole `?.let { }` expression short-circuits to `null` and the block never runs. If it is non-null, `let` invokes the lambda with the value as its single argument, conventionally named `it`, smart-cast to the non-null type. It returns whatever the lambda's last expression produces (so it's an expression, not just a statement). It's the idiomatic way to run code on a nullable only when present, e.g. `name?.let { println(it.length) }`, replacing an `if (name != null)` block — especially useful for nullable `var` properties where a plain smart cast isn't allowed.
code
kotlin · 5 linesval name: String? = readLine()
val len: Int? = name?.let {
println("got: $it")
it.length // returned to len
}go deeper
Knows the block runs only on non-null and it is the value; can replace an if (x != null) with it.
Explains the smart-cast of it, the return value, and why it's needed for mutable var properties.
Frames it as ?. + the let scope function, contrasts with also/apply/run, and discusses readability trade-offs.
Reasons about when the idiom hurts readability vs. an early-return guard, and team conventions for naming it in chains.
## What `?.let { }` is This idiom is built from two pieces: - **`?.` — the safe-call operator.** `a?.b` evaluates to `null` if `a` is `null`; otherwise it evaluates `a.b`. It never throws a `NullPointerException` for the `a`-is-null case. - **`let` — a scope function** from the Kotlin standard library. `let` is an extension function `fun <T, R> T.let(block: (T) -> R): R`. It takes the receiver, passes it to `block` as an argument, and returns whatever `block` returns. Combined, `value?.let { ... }`: 1. If `value` is `null`, the `?.` short-circuits and the lambda is **never executed**; the expression's value is `null`. 2. If `value` is non-null, `let` runs the lambda with the value bound to **`it`**, which is **smart-cast to the non-null type** (e.g. `String?` becomes `String`). ## Why `it` `let` passes its receiver as a **lambda argument**, so inside you refer to it via the implicit single-parameter name `it`. You can rename it for clarity: `value?.let { v -> ... }`. ## Why use it instead of `if (value != null)` For a **local `val`**, a plain `if (value != null) { value.foo() }` already smart-casts. But for a **mutable `var`** (especially a property that another thread could change), the compiler refuses to smart-cast because the value might change between the check and the use. `?.let` captures the value once as a stable parameter, so it's both safe and idiomatic. ```kotlin class Config { var timeout: Int? = null } fun show(c: Config) { // c.timeout?.foo() would not smart-cast (mutable property) c.timeout?.let { t -> println("timeout is $t") // t: Int (non-null) } } ``` ## Return value Because `let` returns the lambda result, `?.let` is an **expression**: ```kotlin val upper: String? = name?.let { it.uppercase() } ``` If `name` is null, `upper` is null; otherwise it is the uppercased name. ## Key APIs/keywords named `?.` (safe call), `let`, `it`, smart cast, scope function, Elvis `?:` (often paired to supply a fallback).
- If `name` is null in `name?.let { it.length }`, what is the result?`null` — the safe call short-circuits and the lambda never runs, so the whole expression is `null`.
- Can you rename `it`?Yes: `name?.let { n -> n.length }`. Naming the parameter improves readability, especially in nested lets.
Like 'if there's a letter in the mailbox, open and read it' — no letter, nothing happens.
saying these in an interview costs you the question
- Saying the lambda always runs even when the receiver is null
- Thinking `it` is still nullable inside the block
- Claiming `?.let` returns the receiver (that's `also`, not `let`)
- Confusing `let` with `apply`/`run` (which use `this`, not `it`)