Question
I would like clear examples showing when to use each of Kotlin’s scope functions: run, let, apply, also, and with.
I have already read explanations about the differences between these functions, but I still do not have practical examples that make their use cases easy to understand.
Short Answer
By the end of this page, you will understand what Kotlin scope functions do, how run, let, apply, also, and with differ, and when each one is the best fit. You will also see practical examples, common mistakes, and a small project that helps you use them naturally in real Kotlin code.
Concept
Kotlin’s scope functions let you execute a block of code using an object in a shorter, more readable way.
The five commonly used scope functions are:
letrunapplyalsowith
They all help you work with an object inside a block, but they differ in two important ways:
-
How the object is referenced inside the block
- As
it - As
this
- As
-
What the function returns
- The object itself
- The result of the block
That is the whole idea.
Why this matters
In real code, these functions help with tasks like:
- configuring objects
- avoiding repeated variable names
- handling nullable values safely
- performing extra side effects such as logging
- grouping related operations
The core differences
| Function |
|---|
Mental Model
Think of a scope function like inviting an object into a small workspace.
You choose:
- what name the object has in the workspace:
thisorit - what you take out of the workspace: the object itself or a new result
Analogy: working at a desk
Imagine you place an object on your desk.
apply: You adjust the object, set it up, and then take the same object away.also: You keep the object the same, but make notes about it on the side, then take the same object away.let: You examine the object and produce some new result.run: You work with the object closely as if it were your current context, and produce some new result.with: Similar torun, but you already have the object and say, “work with this object for a moment.”
Quick intuition
apply= configure this objectalso= do something extra with this objectlet= use this object and produce a result
Syntax and Examples
1. let
let uses it and returns the result of the block.
val name: String? = "Kotlin"
val length = name?.let {
it.length
}
println(length) // 6
This is common for nullable values. If name is not null, the block runs. The result is the length.
2. run
run uses this and returns the result of the block.
data class User(val firstName: String, val lastName: String)
val user = User("Ada", "Lovelace")
val fullName = user.run {
"$firstName $lastName"
}
println(fullName) // Ada Lovelace
Inside run, you can access members directly because the receiver is .
Step by Step Execution
Consider this example:
data class Box(var value: Int = 0)
val result = Box().apply {
value = 10
}.also {
println("Box after apply: $it")
}.run {
value * 2
}
println(result)
Step by step
Box()creates a new object:
Box(value = 0)
apply { value = 10 }runs.- Inside
apply,thisis theBoxobject. valuebecomes10.applyreturns the same object.
- Inside
Now the object is:
Box(value = 10)
Real World Use Cases
let
Useful when working with nullable values or performing a short transformation.
val email: String? = getEmailFromApi()
val domain = email?.let {
it.substringAfter("@")
}
Common uses:
- null-safe processing
- converting one value into another
- limiting variable scope
run
Useful when you want to do several operations on an object and return one computed result.
data class Order(val price: Double, val tax: Double)
val order = Order(100.0, 10.0)
val total = order.run {
price + tax
}
Common uses:
- deriving values from an object
- formatting output
- combining several member accesses into one result
apply
Useful for building or configuring objects.
intent = android.content.Intent().apply {
action =
putExtra(, )
}
Real Codebase Usage
In real projects, developers usually choose scope functions based on intent, not just syntax.
1. Object configuration with apply
A very common pattern is object creation plus setup.
data class ApiConfig(var baseUrl: String = "", var timeout: Int = 0)
val config = ApiConfig().apply {
baseUrl = "https://api.example.com"
timeout = 30
}
This reads like: create this object, configure it, and keep it.
2. Null handling with let
Developers often use let after a safe call.
val token: String? = getToken()
token?.let {
println("Using token: $it")
}
This avoids manual null checks.
3. Side effects with also
also is common in chains where you want extra behavior without changing the result.
Common Mistakes
1. Using the wrong function for configuration
If you want to configure an object and keep that object, use apply, not run.
Broken idea:
data class User(var name: String = "")
val user = User().run {
name = "Mia"
}
println(user) // user is Unit, not User
Why it fails:
runreturns the last expression in the block- Here, the assignment returns
Unit
Correct:
val user = User().apply {
name = "Mia"
}
2. Using let when it makes code unclear
Too many nested it references become confusing.
val text: String? = "hello"
text?.let {
println(it)
listOf(it.length).also {
println(it)
}
}
Comparisons
| Function | Receiver name | Returns | Best for | Example intent |
|---|---|---|---|---|
let | it | lambda result | null checks, transformations | use this value and produce something |
run | this | lambda result | compute using object context | calculate something from this object |
apply | this | object itself | configuration | set up this object |
also |
Cheat Sheet
Quick rules
let= useit, return resultrun= usethis, return resultapply= usethis, return objectalso= useit, return objectwith= usethis, return result
Best use cases
let: null-safe calls and transformationsrun: compute a result from an objectapply: configure an objectalso: logging or side effectswith: group operations on an existing object
Return behavior
val x = "hello".let { it.length } // Int
val y = "hello".run { length }
z = Person().apply { name = }
w = Person().also { println(it) }
q = with() { length }
FAQ
When should I use let in Kotlin?
Use let when you want to work with a value, especially a nullable one, and return a new result from it.
What is the difference between apply and also in Kotlin?
Both return the original object. apply is mainly for configuring the object, while also is mainly for side effects like logging.
What is the difference between run and with in Kotlin?
Both use this and return the lambda result. run is called on an object as an extension function, while with takes the object as an argument.
Why is let often used with nullable values?
Because value?.let { ... } only runs the block if value is not null.
Which scope function should I use to initialize an object?
Usually apply, because it lets you set properties and returns the configured object.
Mini Project
Description
Create a small Kotlin program that builds a user profile, logs progress, safely handles an optional nickname, and generates a final summary string. This project demonstrates the natural role of apply, also, let, run, and with in one practical flow.
Goal
Build and configure a profile object, perform side effects, handle a nullable value, and produce summary text using the correct Kotlin scope functions.
Requirements
- Create a
Profiledata class with mutable properties for name, age, city, and nickname. - Use
applyto create and configure aProfileobject. - Use
alsoto print the object after configuration. - Use
letto safely format the nickname only if it is not null. - Use
runorwithto generate a final summary string from the profile.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.
Android AlarmManager Example: Scheduling Tasks with AlarmManager
Learn how to use Android AlarmManager to schedule tasks, set alarms, and handle broadcasts with a simple beginner example.