skip to content

let

let passes the object as it and returns the lambda's result, which makes it the natural fit for transforming a value. Combined with a safe call, x?.let { } is the standard way to run code only when a value is present.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What does Kotlin's let scope function do, and how do you access the value inside its lambda?

level: juniorimportance: must knowfreq 80%

answer

  1. Receiver in as it, lambda result out
  2. inline extension fun T.let(block: (T) -> R): R
  3. Does NOT return the original object
  4. it is the implicit single-param name
  5. Transform / scope a temporary name

basics

~10 s

let runs a block of code on a value. Inside the block the value is called it, and let returns whatever the last line of the block produces.

solid answer

~30 s

let is an extension function callable on any value. It takes a lambda and passes the receiver object into that lambda as the implicit parameter it. The return value of let is the result of the lambda (its last expression), NOT the original object. So `val len = name.let { it.length }` gives the length. This makes let useful for transforming a value into something else and for scoping a temporary name to a small block. You can rename it: `name.let { n -> n.length }`. Contrast with also/apply, which pass the object as receiver and return the object itself.

code

kotlin · 5 lines
kotlin
val name = "Kotlin"
val length = name.let { it.length }        // 6  (lambda result returned)
val shout  = name.let { s -> s.uppercase() } // "KOTLIN" (renamed param)
println(length)
println(shout)

go deeper

for a junior

Knows let passes the value as it and returns the lambda result.

for a middle

Distinguishes let (it / lambda result) from apply/also (this or it / receiver) and uses it to transform values.

for a senior

Mentions the inline signature, no allocation, and chooses let deliberately for value transformation and scoping.

for a principal

Frames let within the full scope-function decision matrix and sets team conventions on when renaming it improves readability.

## What `let` is `let` is one of Kotlin's five **scope functions** (the others are `run`, `with`, `apply`, `also`). It is defined in the standard library as an **inline extension function** on a generic receiver: ```kotlin public inline fun <T, R> T.let(block: (T) -> R): R = block(this) ``` Because it is `inline`, the lambda body is inlined at the call site — there is no runtime lambda object allocation. ## The two defining behaviours 1. **The receiver is passed as the lambda argument `it`.** Inside the block you refer to the object as `it` (the implicit single-parameter name), not as `this`. You may rename it: `value.let { v -> ... }`. 2. **`let` returns the lambda result.** Whatever the last expression of the block evaluates to becomes the value of the whole `let` call. It does NOT return the original receiver. ```kotlin val name = "Kotlin" val length: Int = name.let { it.length } // returns 6 val upper: String = name.let { it.uppercase() } // returns "KOTLIN" ``` ## Typical uses - **Transform a value** into a different type or shape and capture the result. - **Introduce a local name** for an expression so you don't repeat or recompute it: ```kotlin val result = computeExpensive().let { x -> x * x + x } ``` - **Run a block only on a non-null value** via `?.let { }` (covered separately). ## `it` vs renaming `it` is implicit only when the lambda has exactly one parameter. For readability inside longer blocks, give it a name: `user.let { u -> ... }`. ## Contrast (so you pick the right tool) - `let` / `run`: return the **lambda result**. - `apply` / `also`: return the **receiver object** (used for configuration/side effects). - `let` / `also`: expose the object as **`it`**; `run` / `with` / `apply`: as **`this`**.

  • Does let return the original object or the lambda result?
    The lambda result — the value of the last expression in the block. Use also if you want the original object back.
  • Can you rename it inside let?
    Yes: name.let { n -> n.length }. it is just the default name when the lambda has one parameter.

let is like a vending machine: you put the object in (as it) and get back whatever the block dispenses, not your original coin.

saying these in an interview costs you the question

  • Saying let returns the original receiver (that's apply/also)
  • Referring to the value as this inside let (it's it)
  • Claiming let can take multiple parameters
  • Confusing let with with (with takes the object as an argument, not as a receiver call)

context

open as a page

Explain the x?.let { } idiom. What exactly happens when x is null versus non-null?

level: middleimportance: must knowfreq 85%

basics

~10 s

x?.let { } runs the block only when x is not null. If x is null, the block is skipped and the whole expression is null. Inside the block x is non-null.

open as a page

A teammate writes `val updated = user.let { it.copy(active = true) }` then later uses user expecting it to be active. What's wrong, and how does let's return semantics explain it?

level: middleimportance: should knowfreq 60%

basics

~10 s

let returns the block's result, not the original object. The new active user is in updated; the original user is untouched. They should use updated, or pick a function that returns the object.

open as a page

How does let behave when chained on collections or in transformation pipelines, and how is it different from map?

level: seniorimportance: should knowfreq 45%

basics

~20 s

let treats the whole value as one thing: it passes the entire object in as it and returns one result. map transforms each element of a collection. So list.let acts on the list itself; list.map acts on items.

open as a page

When does overusing let hurt code quality, and what are its readability and nesting pitfalls (especially nested ?.let chains)?

level: seniorimportance: nice to knowfreq 35%

basics

~20 s

let is overused when it wraps simple calls or stacks many nested blocks. That makes code hard to read, with ambiguous its and deep nesting. Plain ifs, named variables, or early returns are often clearer.

open as a page