0
0
KotlinHow-ToBeginner · 4 min read

How to Choose Scope Function in Kotlin: Guide with Examples

Choose a Kotlin scope function based on your goal: use let to work with a nullable object and return a result, apply to configure an object and return it, run to execute a block and return its result, with to call multiple methods on an object without extension, and also to perform additional actions without changing the object. The choice depends on whether you need the object as this or it, and what you want to return.
📐

Syntax

Kotlin has five main scope functions: let, run, with, apply, and also. Each has a similar syntax but differs in how the object is referenced and what is returned.

  • let: object is it, returns lambda result
  • run: object is this, returns lambda result
  • with: takes object as argument, this inside, returns lambda result
  • apply: object is this, returns the object
  • also: object is it, returns the object
kotlin
val resultLet = "Hello".let {
    println(it)
    it.length
}

val resultRun = "Hello".run {
    println(this)
    length
}

val resultWith = with("Hello") {
    println(this)
    length
}

val resultApply = StringBuilder().apply {
    append("Hello")
    append(" World")
}

val resultAlso = "Hello".also {
    println(it)
}
Output
Hello Hello Hello Hello
💻

Example

This example shows how to choose scope functions based on what you want to do: configure an object, run code with a result, or perform side effects.

kotlin
data class Person(var name: String, var age: Int)

fun main() {
    // Use apply to configure and return the object
    val person = Person("", 0).apply {
        name = "Alice"
        age = 25
    }
    println("Person after apply: $person")

    // Use let to work with nullable and return a result
    val nullableName: String? = person.name
    val length = nullableName?.let {
        println("Name is $it")
        it.length
    } ?: 0
    println("Name length: $length")

    // Use run to execute block with this and return result
    val greeting = person.run {
        "Hello, my name is $name and I am $age years old."
    }
    println(greeting)

    // Use also to perform side effects and return the object
    val updatedPerson = person.also {
        println("Updating age")
        it.age = 26
    }
    println("Person after also: $updatedPerson")

    // Use with to call multiple methods on an object
    val infoLength = with(person) {
        println("Person info: $name, $age")
        name.length + age
    }
    println("Info length: $infoLength")
}
Output
Person after apply: Person(name=Alice, age=25) Name is Alice Name length: 5 Hello, my name is Alice and I am 25 years old. Updating age Person after also: Person(name=Alice, age=26) Person info: Alice, 26 Info length: 31
⚠️

Common Pitfalls

Common mistakes include confusing this and it references, expecting the wrong return value, or using a scope function that doesn't fit the task.

  • Using apply when you need a result from the lambda (it returns the object, not the lambda result).
  • Using let when you want to configure an object (it returns the lambda result, not the object).
  • Forgetting with is not an extension function but a regular function taking the object as argument.
kotlin
val list = mutableListOf<Int>()

// Wrong: apply returns the object, not the lambda result
val sizeWrong = list.apply {
    add(1)
    add(2)
    size // this is ignored
}
println(sizeWrong) // prints the list, not size

// Right: run returns the lambda result
val sizeRight = list.run {
    add(3)
    add(4)
    size
}
println(sizeRight) // prints 4
Output
[1, 2, 3, 4] 4
📊

Quick Reference

Scope FunctionObject ReferenceReturn ValueUse Case
letitLambda resultNullable object handling, chaining calls
runthisLambda resultExecute block with object as receiver, return result
withthisLambda resultCall multiple methods on object, not extension
applythisObjectConfigure object, return it for chaining
alsoitObjectPerform side effects, return object

Key Takeaways

Pick scope functions based on whether you need the object as 'this' or 'it' inside the block.
Use 'apply' and 'also' when you want to return the original object after configuration or side effects.
Use 'let', 'run', or 'with' when you want to return the result of the lambda block.
Remember 'with' is not an extension function but takes the object as an argument.
Avoid mixing up return values to prevent bugs and unexpected behavior.