45. Scope Functions

The standard library offers five scope functions — let, run, with, apply, and also — that execute a block of code in the context of an object. They differ in two ways: how the object is referenced inside the block (as this or as it) and what the block returns (the lambda result or the object itself).

Function Object reference Returns
let it lambda result
run this lambda result
with this lambda result
apply this the object
also it the object
class Config {
    var host: String = ""
    var port: Int = 0
}

fun main() {
    // let: receiver is `it`, returns lambda result. Great for null checks.
    val name: String? = "Kotlin"
    val length = name?.let {
        println("got: $it")
        it.length
    }
    println("length: $length")

    // run: receiver is `this`, returns lambda result.
    val area = run {
        val w = 4
        val h = 5
        w * h
    }
    println("area: $area")

    // with: receiver is `this` (passed as argument), returns lambda result.
    val cfg = Config()
    val summary = with(cfg) {
        host = "localhost"
        port = 8080
        "$host:$port"
    }
    println("summary: $summary")

    // apply: receiver is `this`, returns the object. Ideal for configuring.
    val server = Config().apply {
        host = "example.com"
        port = 443
    }
    println("server: ${server.host}:${server.port}")

    // also: receiver is `it`, returns the object. Ideal for side-effects.
    val numbers = mutableListOf(1, 2, 3).also {
        println("created list of size ${it.size}")
    }
    println("numbers: $numbers")
}

In practice: reach for ?.let { } to run code only on a non-null value, apply { } to configure a freshly created object and get it back, and also { } to slip in a side-effect (logging, validation) without changing the value flowing through.

Running it:

$ kotlin run
got: Kotlin
length: 6
area: 20
summary: localhost:8080
server: example.com:443
created list of size 3
numbers: [1, 2, 3]
← Prev Index Next →