What do Kotlin's run { } and with(x) { } scope functions do, and what does each return?
answer
- run = extension; with = takes arg
- both: this receiver, return block result
- x?.run for nullable; with can't guard null
- return block result != apply (returns object)
- use to map object -> value
basics
~20 sBoth run a block of code on an object you can call this on. run is called on the object; with takes the object as an argument. Both return the last expression in the block.
solid answer
~40 srun and with are scope functions that expose the target object as the this receiver inside the lambda, so you call its members without a name prefix. Both return the lambda's last expression (the block result), not the object. Use x.run { } when you already have a receiver and want to configure-and-compute in one expression; use with(x) { } as a non-extension call that reads like "with this object, do...". x.run { } is null-safe-friendly via x?.run { }, while with(x) cannot guard null because x is just a regular argument. Because they return the block result, run/with are ideal for mapping an object to a different value, initializing a val, or grouping calls whose final line produces the result.
code
kotlin · 6 linesdata class User(val name: String, val age: Int)
val u = User("Ada", 36)
val a = u.run { "$name ($age)" } // "Ada (36)"
val b = with(u) { "$name ($age)" } // "Ada (36)"
val c: String? = (null as User?)?.run { name } // null, block skippedgo deeper
Knows both expose this and return the block result, and that run is an extension while with takes an argument.
Picks run/with when transforming an object to a value; uses x?.run for nullable receivers; avoids them when they wanted the object back.
Articulates that with(x) and x.run are behaviorally equivalent and chooses by call-site readability and null-safety needs.
Sets team conventions for scope-function usage, weighing readability, nullability, and chainability across a codebase.
## What scope functions are A **scope function** runs a lambda (a block of code) in the context of an object, giving you a temporary scope where that object is available without repeating its name. Kotlin's standard library has five: `let`, `run`, `with`, `apply`, `also`. They differ on two axes: **how the object is referenced** inside the block (`this` receiver vs. `it` argument) and **what the call returns** (the object itself vs. the block result). This leaf covers `run` and `with`. ## run `run` is an extension function. The object it's called on becomes the **`this` receiver** inside the lambda, so you can access members directly (e.g. `length` instead of `s.length`). It **returns the last expression** of the block (the lambda result). ```kotlin val message = user.run { // 'this' is user; name and age are user's members "$name is $age years old" // returned } ``` ## with `with` is **not** an extension; it's a top-level function taking the object as its first argument: `with(x) { ... }`. Inside, `x` is the **`this` receiver**, just like `run`. It also **returns the last expression**. ```kotlin val summary = with(user) { "$name, $age" // 'this' is user; returned } ``` So `with(x) { ... }` and `x.run { ... }` are nearly identical in behavior — same receiver, same return. The difference is **call style**: `run` is an extension (chains nicely, supports null safety); `with` is a plain function call. ## Nullable: x?.run Because `run` is an extension, you can use the safe-call operator: `x?.run { ... }`. The block executes only if `x` is non-null; otherwise the whole expression is `null`. `with(x)` has no such guard — `x` is just an argument, so you'd need a manual null check. ```kotlin val len: Int? = nullableString?.run { length * 2 } ``` ## Return value matters Both return the **block result**, which is what distinguishes them from `apply`/`also` (which return the object). Use `run`/`with` to **transform** an object into another value or to compute a result from several member calls. ## Quick comparison - `x.run { }` — receiver `this`, returns block result, extension (nullable-friendly). - `with(x) { }` — receiver `this`, returns block result, non-extension. - `x.apply { }` — receiver `this`, returns `x` (the object). - `x.let { }` / `x.also { }` — argument `it`, returns block result / object respectively.
- If with(x) and x.run are so similar, why have both?with reads as a top-level 'with this object, do...' statement and is conventional when you don't have a chain; run is an extension so it chains and supports x?.run for null safety.
- What does run return if the block's last line is an assignment?Unit. Assignments are statements of type Unit in Kotlin, so the block result is Unit — usually a sign you wanted apply (returns the object) instead.
with(x) { } is like saying 'with this whiteboard, write...'; x.run { } is the whiteboard itself running your instructions — same result, different phrasing.
saying these in an interview costs you the question
- Saying run/with return the object (they return the block result)
- Claiming with is an extension function (it is a top-level function taking an argument)
- Thinking the object is accessed via 'it' inside run/with (it's 'this')
- Believing with(x) supports a safe call like with?(x)