Question

How to Work with Multiple Nullable Variables in Kotlin Using let

Question

In Kotlin, is there a clean way to use let with multiple nullable variables at the same time?

For example:

fun example(first: String?, second: String?) {
    first?.let {
        second?.let {
            // Do something only if both are not null
        }
    }
}

I am looking for a shorter or clearer approach, something conceptually like this:

fun example(first: String?, second: String?) {
    first?.let && second?.let {
        // Do something only if both are not null
    }
}

Is there an idiomatic Kotlin way to run code only when multiple nullable values are all non-null?

Short Answer

By the end of this page, you will understand how Kotlin's null-safety works with multiple nullable values, how let behaves, and the common idiomatic ways to execute code only when several variables are not null. You will also see practical alternatives such as if checks, nested let, early returns, and helper patterns.

Concept

Kotlin has built-in null-safety features to help prevent NullPointerException. A nullable type is written with ?, such as String?, which means the variable may contain either a String or null.

The let function is often used with the safe-call operator ?.:

value?.let {
    // runs only if value is not null
}

This is useful for a single nullable value. But when you have multiple nullable variables, Kotlin does not provide special syntax like:

first?.let && second?.let { ... }

That syntax does not exist because let is just a normal function call on a value, not a language-level boolean operator.

When you want to run code only if several values are non-null, the main idea is simple:

  • check that all required values are not null
  • then use them in a non-null context

In Kotlin, the most common ways to do that are:

  • nested let
  • a normal if null check
  • early return with guard clauses
  • helper functions for repeated patterns

This matters in real programming because nullable values are everywhere:

  • API responses
  • form input
  • database fields
  • optional configuration
  • parsed JSON values

Knowing how to combine null checks cleanly makes your code safer and easier to read.

Mental Model

Think of nullable values as keys that may or may not exist.

  • first?.let { ... } means: "If key first exists, unlock this block."
  • second?.let { ... } means the same for second.

If you need both keys, then both must be present before you can open the door.

You can do that in different ways:

  • check one key, then the other
  • check both first with an if
  • return early if one key is missing

So the real question is not "How do I chain lets with &&?" but rather:

What is the clearest way to guarantee all required values are non-null before running the code?

Take Quiz

Syntax and Examples

The simplest valid approaches in Kotlin are below.

1. Nested let

fun example(first: String?, second: String?) {
    first?.let { f ->
        second?.let { s ->
            println("Both values exist: $f and $s")
        }
    }
}

This works, but nesting can become harder to read if you have many nullable values.

2. Use if to check both values

fun example(first: String?, second: String?) {
    if (first != null && second != null) {
        println("Both values exist: $first and $second")
    }
}

This is often the clearest option. Inside the if block, Kotlin smart-casts both values to non-null.

3. Early return with guard clauses

fun example(first: String?, second: String?) {
    if (first == null || second == null) return

    println("Both values exist: $first and $second")
}

This style is very common in real code because it avoids nesting.

4. Use one let inside another but name values clearly

fun example(first: String?, second: String?) {
    first?.let { firstValue ->
        second?.let { secondValue ->
            println(firstValue.length + secondValue.length)
        }
    }
}

Using named lambda parameters like firstValue and secondValue is clearer than relying on the default it.

5. Create a helper function for reuse

inline fun <A, B> ifNotNull(a: A?, b: B?, block: (A, B) -> Unit) {
    if (a != null && b != null) {
        block(a, b)
    }
}

fun example(first: String?, second: String?) {
    ifNotNull(first, second) { f, s ->
        println("Both values exist: $f and $s")
    }
}

This can be useful if your project frequently needs this pattern.

Step by Step Execution

Consider this example:

fun example(first: String?, second: String?) {
    if (first != null && second != null) {
        println(first.uppercase())
        println(second.uppercase())
    }
}

Now imagine this call:

example("hello", "world")

Step by step

  1. first receives "hello".
  2. second receives "world".
  3. Kotlin checks first != null.
    • This is true.
  4. Kotlin checks second != null.
    • This is also true.
  5. Since both are true, the if block runs.
  6. Inside the block, Kotlin smart-casts both variables to non-null String.
  7. first.uppercase() prints HELLO.
  8. second.uppercase() prints WORLD.

Now imagine this call instead:

example("hello", null)

What changes?

  1. first != null is true.
  2. second != null is false.
  3. Because the whole condition uses &&, the block does not run.
  4. No uppercase call happens, so there is no risk of using a null value.

This is the core behavior you want when working with multiple nullable variables.

Real World Use Cases

This pattern appears often in everyday Kotlin code.

Form validation

if (email != null && password != null) {
    login(email, password)
}

Only proceed when the required fields exist.

API response handling

if (user.id != null && user.name != null) {
    saveUser(user.id, user.name)
}

Some API fields may be optional, so you check before using them.

Database values

if (row.title != null && row.createdAt != null) {
    println("${row.title} created at ${row.createdAt}")
}

Useful when reading partially populated records.

Configuration loading

if (host != null && port != null) {
    connect(host, port)
}

A program may need multiple configuration values before it can continue.

UI logic

if (selectedUser != null && selectedAccount != null) {
    showDetails(selectedUser, selectedAccount)
}

Only render a screen when all required state is available.

Take Quiz

Real Codebase Usage

In real projects, developers usually choose the style that makes the intent easiest to read.

Common pattern: guard clauses

fun process(first: String?, second: String?) {
    if (first == null || second == null) return

    println("Processing $first and $second")
}

Why it is popular:

  • avoids deep nesting
  • keeps the main logic at the left margin
  • makes missing data handling explicit

Common pattern: validation before work

fun createUser(name: String?, email: String?) {
    require(name != null && email != null) { "Name and email are required" }

    println("Creating user: $name, $email")
}

This is useful when null values are considered invalid input.

Common pattern: helper for repeated null checks

inline fun <A, B, R> withNotNull(a: A?, b: B?, block: (A, B) -> R): R? {
    return if (a != null && b != null) block(a, b) else null
}

Teams may create helpers like this when the same null-check pattern appears many times.

Common pattern: avoid overusing nested scope functions

Kotlin has several scope functions:

  • let
  • run
  • apply
  • also
  • with

In real codebases, developers try not to stack too many of them together because readability drops quickly.

Practical rule

If a normal if is clearer than nested let, use if. Idiomatic Kotlin values readability, not cleverness.

Common Mistakes

1. Expecting special syntax for multiple let calls

Broken idea:

fun example(first: String?, second: String?) {
    first?.let && second?.let {
        println("Won't compile")
    }
}

Why it fails:

  • let is not a boolean expression by itself
  • && works with boolean values, not function calls in this form

2. Using nested it and losing track of values

Confusing code:

fun example(first: String?, second: String?) {
    first?.let {
        second?.let {
            println(it)
        }
    }
}

Problem:

  • the inner it hides the outer it
  • it becomes unclear which value you are using

Better:

fun example(first: String?, second: String?) {
    first?.let { f ->
        second?.let { s ->
            println("$f $s")
        }
    }
}

3. Using !! when it is not necessary

Risky code:

fun example(first: String?, second: String?) {
    if (first != null && second != null) {
        println(first!!)
        println(second!!)
    }
}

Why avoid it:

  • !! can throw an exception
  • inside the if, Kotlin already knows they are non-null

Better:

fun example(first: String?, second: String?) {
    if (first != null && second != null) {
        println(first)
        println(second)
    }
}

4. Overcomplicating simple null checks

Sometimes beginners try to use let everywhere because it looks more "Kotlin-like". But this is often easier:

if (first != null && second != null) {
    doSomething(first, second)
}

Simple code is often the best code.

5. Forgetting smart casts only work in suitable conditions

Kotlin smart-casts when it can safely prove a variable is non-null. In simple local checks, this usually works well. But with mutable properties or complex logic, you may need to store values in local variables first.

Comparisons

ApproachExampleBest forProsCons
Nested letfirst?.let { f -> second?.let { s -> ... } }Short nullable chainsSafe and expressiveCan become deeply nested
if null checkif (first != null && second != null) { ... }Most common casesVery clear, smart-cast worksSlightly more verbose
Guard clause`if (first == nullsecond == null) return`Functions that should stop early
Helper functionifNotNull(first, second) { f, s -> ... }Repeated patternsReusable and clean at call siteAdds abstraction
!! operatordoSomething(first!!, second!!)Rare cases onlyShort syntaxUnsafe, can throw exceptions

let vs if

  • Use let when you want to create a temporary non-null scope for one value.
  • Use if when checking multiple conditions is clearer.

Guard clause vs nested block

  • Guard clauses reduce indentation.
  • Nested blocks are okay for very small logic, but can become harder to follow.
Take Quiz

Cheat Sheet

Quick syntax

One nullable value

value?.let { nonNullValue ->
    println(nonNullValue)
}

Two nullable values with if

if (first != null && second != null) {
    doSomething(first, second)
}

Two nullable values with early return

if (first == null || second == null) return
doSomething(first, second)

Two nullable values with nested let

first?.let { f ->
    second?.let { s ->
        doSomething(f, s)
    }
}

Rules to remember

  • ?. calls something only if the value is not null.
  • let is a scope function, not special null-check syntax.
  • There is no Kotlin syntax like first?.let && second?.let.
  • For multiple nullable values, if is often the clearest choice.
  • Prefer named lambda parameters over nested it.
  • Avoid !! unless you truly want to throw on null.

Handy reusable helper

inline fun <A, B> ifNotNull(a: A?, b: B?, block: (A, B) -> Unit) {
    if (a != null && b != null) block(a, b)
}

Edge case

Smart casts may not work as expected with mutable properties. In that case, copy the values into local variables first.

FAQ

Can Kotlin use one let for two nullable variables?

Not directly with special syntax. You usually use nested let, an if check, or a helper function.

What is the most idiomatic way to check two nullable values in Kotlin?

In many cases, a simple if (a != null && b != null) is the clearest and most idiomatic choice.

Is nested let bad in Kotlin?

No, but too much nesting can hurt readability. For two or more variables, if or guard clauses are often easier to read.

Why does first?.let && second?.let not work?

Because let is a function call, not a boolean operator. The && operator only combines boolean expressions.

Should I use !! after checking for null?

Usually no. If Kotlin smart-casts the variables inside the checked block, !! is unnecessary.

How do I avoid deeply nested nullable code?

Use guard clauses, local variables, or helper functions to keep the code flat and readable.

Can I write a helper for multiple non-null values?

Yes. Many developers create small utility functions such as ifNotNull(a, b) { ... } when the pattern appears often.

Take Quiz

Mini Project

Description

Build a small Kotlin function that formats a contact card only when all required nullable inputs are available. This demonstrates how to safely combine multiple nullable values without using unsafe operators.

Goal

Create a function that prints a contact summary only when name and email are both non-null.

Requirements

  • Write a function that accepts name: String? and email: String?.
  • Return early if either value is null.
  • Print a formatted contact summary when both values exist.
  • Call the function with both valid and null inputs to verify the behavior.
Take Quiz

Keep learning

Related questions

View more