← Back to Kotlin Course | Chapter 10: Extension Functions & Properties | Lesson 3 of 6

Scope Functions Deep Dive

Scope functions are a small family of tools that all run a block of code close to one value, but each one hands back a slightly different result.

Comparing Return Values

apply and also always return the original receiver object, while let, run, and with return whatever the lambda block itself produces as its last expression.

Example: Comparing Return Values

markup
fun main() {
    val listApply = mutableListOf(1, 2).apply { add(3) }
    val sizeRun = mutableListOf(1, 2).run { add(3); size }
    println("apply result: $listApply")
    println("run result: $sizeRun")
}

Comparing this vs it

apply, run, and with make the receiver available as this (often omitted), while let and also make it available as it, an explicit parameter name.

Example: Comparing this vs it

markup
data class Box(var content: String)

fun main() {
    val box = Box("empty")
    box.apply { content = "full" } // this.content
    box.also { println("Box now has: ${it.content}") } // it.content
}

Choosing let for Transformation

let is ideal when you want to transform a value into something else and get that new result back, especially in combination with a safe call on a nullable value.

Example: Choosing let for Transformation

markup
fun main() {
    val name: String? = "kotlin"
    val length = name?.let { it.length }
    println("Length: $length")
}

Choosing apply for Configuration

apply is ideal when configuring an object's properties and you want the fully configured object itself back at the end, without needing a separate computed result.

Example: Choosing apply for Configuration

markup
class Settings {
    var darkMode = false
    var fontSize = 12
}

fun main() {
    val settings = Settings().apply {
        darkMode = true
        fontSize = 16
    }
    println("Dark mode: ${settings.darkMode}, Font size: ${settings.fontSize}")
}
Common Mistakes
  1. Choosing the wrong scope function for the job, such as using apply when the intended result was the block's own computed value (which needs run or let instead).
  2. Nesting scope functions deeply, making it hard to tell which this/it belongs to which block.
  3. Using a scope function purely for style when a plain statement would be clearer and just as short.
Chapter Summary
  • let, run, with, apply, and also all execute a block against a receiver but differ in what they return and whether the receiver is this or it.
  • apply and also return the original receiver; let, run, and with return the block's result.
  • apply, run, and with expose the receiver as this; let and also expose it as it.
  • Choosing the right scope function comes down to what you need back: the original object, or a transformed result.
🔒

Chapter Quiz — Complete all 6 topics to unlock

0/6 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.