Multiple Variables in let: A Problem You Haven't Noticed
Have you ever been writing Kotlin and suddenly realized: "I need to handle two nullable variables at once"? You reach for your favorite let, but... stop. let only works with one variable. What do you do if you have two, three, or more?
This question is one of the most popular on Stack Overflow regarding Kotlin. Thousands of developers stumble over this seemingly simple task. And today, we will finally figure out how to solve it elegantly and correctly.
In this article, we won't just give dry examples. We will compare 3 approaches, discuss their pros, cons, and best practices. You will learn which method to choose depending on the situation and stop writing ugly nested let blocks.
Why is the "Multiple Variable let" Problem So Popular?
Kotlin is a language that loves null safety. Scope functions (scope functions) — let, run, with, apply, also — are our secret weapon for safely working with nullable types. But they have a limitation: each function is designed for a single receiver object.
Imagine a scenario: you get two nullable fields from an API — a username and their email. You need to check that both are not null, and only then perform an action (e.g., send a welcome email).
Beginners often write it like this:
val name: String? = getUserName()val email: String? = getUserEmail()
if (name != null && email != null) { sendWelcomeEmail(name, email)}This works, but it's not the Kotlin way. We can make the code more concise and safe. Let's see how.
Approach 1: Nested let — Simple, but Ugly
The most obvious way is to nest one let inside another. This is the first thing that comes to mind:
val name: String? = getUserName()val email: String? = getUserEmail()
name?.let { safeName -> email?.let { safeEmail -> sendWelcomeEmail(safeName, safeEmail) }}Pros:
- Works out of the box, no additional functions required
- Code reads sequentially
Cons:
- As the number of variables increases (3, 4, or more), the code turns into a "pyramid of doom"
- Hard to read and maintain
- Each nested level adds unnecessary indentation
This approach is only suitable for two variables. For three or more, look for an alternative.
Approach 2: Using Pair, Triple, and data class
Kotlin provides built-in Pair and Triple classes. The idea is to combine nullable variables into a single object, and then check it via let.
val name: String? = getUserName()val email: String? = getUserEmail()
val pair = name?.let { n -> email?.let { e -> n to e }}
pair?.let { (safeName, safeEmail) -> sendWelcomeEmail(safeName, safeEmail)}For three variables, use Triple:
val name: String? = getUserName()val email: String? = getUserEmail()val age: Int? = getUserAge()
val triple = name?.let { n -> email?.let { e -> age?.let { a -> Triple(n, e, a) } }}
triple?.let { (safeName, safeEmail, safeAge) -> sendWelcomeEmail(safeName, safeEmail, safeAge)}Pros:
- No deep nesting in the final call
- Destructuring makes the code clean
Cons:
- Internal nesting (when creating Pair/Triple) still exists
Tripleis the maximum for the standard library. For 4+ variables you need