skip to content

Multi-Statement Bodies & Last-Expression Return

A lambda body can have many statements, and its last expression is the result — there is no return statement for it. When you do need an early exit you use a labeled return like return@map, which is the detail people get wrong.

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

questions

5

In a multi-line Kotlin lambda, how is the value the lambda produces determined? Show how this works with a map call.

level: juniorimportance: must knowfreq 78%

answer

  1. Last expression = lambda result
  2. No implicit return keyword in lambdas
  3. Statements (val, assignment) are Unit
  4. Same rule as if/when/= bodies
  5. Wrong last line -> List<Unit>

basics

~10 s

The last line in the lambda's body is its result automatically. You don't write a return keyword. Whatever that final line evaluates to is what the lambda hands back.

solid answer

~40 s

A Kotlin lambda's result is the value of its last expression. There is no implicit `return` keyword inside a lambda body; the final line is simply evaluated and becomes the lambda's return value. Earlier lines run for their side effects or to compute intermediate `val`s. For example, in `list.map { val doubled = it * 2; doubled + 1 }`, each element maps to `doubled + 1` because that is the last expression. Statements that are not expressions (like a `val` declaration or an assignment) have type `Unit` and cannot be the meaningful last line if you need a real value. This 'last expression is the result' rule is the same one used by `if`, `when`, and block bodies of functions written with `=`.

code

kotlin · 5 lines
kotlin
val totals = listOf(1, 2, 3).map {
    val base = it * it   // intermediate computation
    base + 1             // last expression -> mapped value
}
// totals == [2, 5, 10]

go deeper

for a junior

Knows the last expression is the result and that no return keyword is used.

for a middle

Explains expression-vs-statement and how map infers List<Unit> from a Unit last line.

for a senior

Connects it to the unifying last-expression rule across if/when/= bodies and contrasts with non-local return.

for a principal

Frames it as part of Kotlin's expression-oriented design and discusses readability tradeoffs of long multi-statement lambdas vs extracted named functions.

## The rule A **lambda** is a block of code you pass around as a value, written in `{ ... }`. When a lambda has **multiple statements**, Kotlin does NOT require (or allow) a bare `return` to produce its value. Instead, **the value of the last expression in the lambda body is the lambda's result**. ```kotlin val lengths = listOf("a", "bb", "ccc").map { word -> val trimmed = word.trim() // statement 1: declares a val (type Unit as a statement) val n = trimmed.length // statement 2 n * 10 // LAST expression -> this is what the lambda returns } // lengths == [10, 20, 30] ``` ## Expressions vs statements - An **expression** produces a value (`n * 10`, `if (x) 1 else 2`, a function call). - A **statement** is executed for effect and has type `Unit` (`val x = 5`, `println(...)`, an assignment `a = b`). - If your last line is a statement, the lambda returns `Unit`. That is fine for `forEach` but wrong for `map`, where the compiler infers the element type from the last expression. ## Why no `return` keyword? Inside a lambda, a bare `return` would mean **return from the enclosing function** (a non-local return), not from the lambda. To return a value from the lambda itself you either rely on the last-expression rule, or use a **qualified/labeled return** like `return@map value`. ## Same rule everywhere This is the unifying 'last expression' principle in Kotlin: ```kotlin fun f(x: Int) = run { val a = x + 1 a * a // value of run { } and thus of f } val y = if (cond) { log(); 1 } else { 2 } // block-if: last expression wins ``` ## Common pitfall ```kotlin val r = list.map { println(it) // side effect it.length // must be last to be the mapped value } ``` Reversing those two lines would make `map` produce `List<Unit>`.

  • What does map return if the last line of the lambda is println(it)?
    List<Unit>, because println returns Unit and the last expression determines the element type.
  • Can you put a bare `return` on the last line to be explicit?
    No — a bare return targets the enclosing function (non-local return), not the lambda. Use return@map or just the expression.

Like a recipe where the final dish you plate is what you serve — the prep steps before it don't get served, only the last thing on the plate.

saying these in an interview costs you the question

  • Saying you must write `return` inside a lambda
  • Thinking a `val` declaration can be the result value
  • Believing the first line is the result
  • Not knowing map infers element type from the last expression
  • Confusing lambda result with a non-local return

context

open as a page

What does `return@map` do, and how does it differ from a bare `return` inside a lambda passed to map?

level: middleimportance: must knowfreq 70%

basics

~10 s

return@map exits just the current lambda call and supplies that element's result, like 'continue' for the iteration. A bare return would try to exit the whole surrounding function instead.

open as a page

Where does the label in `return@let` or `return@forEach` come from, and when must you declare an explicit label instead?

level: middleimportance: should knowfreq 52%

basics

~20 s

The label after @ is automatically the name of the function the lambda was given to, like let or forEach. You write your own label when the auto name is ambiguous or you have nested lambdas of the same function.

open as a page

How does the expected return type of a higher-order function affect what the last expression of a multi-statement lambda must be? Contrast forEach (Unit) with map (R).

level: seniorimportance: should knowfreq 44%

basics

~20 s

If the function expects a Unit-returning lambda (like forEach), Kotlin ignores whatever the last line produces. If it expects a real value (like map), the last expression must produce that value and its type drives the result type.

open as a page

A teammate writes a 30-line map lambda with several return@map early-exits and intermediate vals. As a reviewer, what concerns and alternatives would you raise about multi-statement-body returns?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

Long lambdas with many early exits are hard to read and test. Suggest extracting the body into a named function, or using an anonymous function with plain returns, so the transformation logic is clear and reusable.

open as a page