Destructuring Declarations

Reviewed & published by Brayan K

A destructuring declaration unpacks an object into several separate variables in a single statement, as in val (x, y) = point, pulling each component out by position.

Part of the free Kotlin course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

It shines with pairs, data classes, map entries, and lambda parameters, making code that handles grouped values dramatically cleaner.

What You'll Learn in This Lesson

1️⃣ Destructuring Pairs & Data Classes

The simplest case unpacks a Pair or Triple into named values. Behind the scenes Kotlin calls component1(), component2(), and so on — so anything that defines those operators can be destructured.

fun main() {
    val point = Pair(3, 7)

    // Destructure a Pair into two named values at once:
    val (x, y) = point
    println("x = $x, y = $y")         // -> x = 3, y = 7

    // Works for a Triple too:
    val (r, g, b) = Triple(255, 128, 0)
    println("$r,$g,$b")               // -> 255,128,0
}

A data class automatically generates those componentN() functions, so its instances destructure too — handy for returning several values from a function:

// Data classes auto-generate component1(), component2(), ...
data class User(val name: String, val age: Int)

fun main() {
    val user = User("Ada", 36)

    // Destructure by POSITION (order of the properties):
    val (name, age) = user
    println("$name is $age")          // -> Ada is 36

    // A function can return several values via a data class:
    fun minMax(): Pair<Int, Int> = Pair(2, 9)
    val (low, high) = minMax()
    println("range $low..$high")       // -> range 2..9
}

2️⃣ Destructuring in for Loops over Maps

Iterating a Map is where destructuring pays off most. Each entry unpacks into (key, value), so the loop reads like plain English instead of repeatedly calling .key and .value.

fun main() {
    val ages = mapOf(
        "Ada" to 36,
        "Linus" to 54
    )

    // Each entry destructures into (key, value):
    for ((name, age) in ages) {
        println("$name -> $age")
    }
}

3️⃣ Skipping with _ and Lambdas

When a type has more components than you need, mark the ones to ignore with an underscore _. You can also destructure right in a lambda's parameter list, which is perfect for collection operations.

data class Person(val first: String, val last: String, val city: String)

fun main() {
    val p = Person("Grace", "Hopper", "NYC")

    // Use _ to SKIP components you don't need:
    val (first, _, city) = p
    println("$first from $city")       // -> Grace from NYC

    // In a lambda, destructure each pair directly:
    val scores = listOf("Ada" to 90, "Bo" to 75)
    scores.forEach { (name, score) ->
        println("$name: $score")
    }
}

Your turn. Fill in the ___ blanks, then run and compare.

// 🎯 YOUR TURN — replace each ___ then run main()
data class Book(val title: String, val pages: Int)

fun main() {
    val book = Book("Kotlin", 320)

    // 1) Destructure the book into title and pages
    val (___, ___) = book            // 👉 (title, pages)

    println("$title has $pages pages")

    // ✅ Expected:
    // Kotlin has 320 pages
}

Mini-Challenge: Coordinate Report

Loop a list of coordinate pairs with destructuring and print each point's sum.

// 🎯 MINI-CHALLENGE: a coordinate report.
// Save as Coords.kt and run main().
//
// Given a list of (x, y) pairs:
//   listOf(1 to 2, 3 to 4, 5 to 6)
// Loop with destructuring (x, y) and print "Point (x, y): sum = x+y"
// for each one.
//
// ✅ Expected output:
//    Point (1, 2): sum = 3
//    Point (3, 4): sum = 7
//    Point (5, 6): sum = 11

fun main() {
    val points = listOf(1 to 2, 3 to 4, 5 to 6)
    // your code here
}

Common Errors (and the fix)

Pro Tips

📋 Quick Reference — Destructuring

Use caseKotlin Syntax
Pair / Tripleval (x, y) = pair
Data classval (name, age) = user
Map entryfor ((k, v) in map)
Skip a slotval (a, _, c) = triple
In a lambda.map { (k, v) -> ... }

🎉 Lesson Complete!

Practice quiz

What does 'val (a, b) = thing' translate into under the hood?

  • val a = thing.component1() and val b = thing.component2()
  • Two calls to toString()
  • A map lookup by name
  • A copy() call

Answer: val a = thing.component1() and val b = thing.component2(). Destructuring calls component1(), component2(), and so on by position.

Is destructuring positional or by name?

  • By the names you choose
  • Strictly positional
  • Alphabetical by property name
  • Random

Answer: Strictly positional. It is strictly positional; the first variable gets component1() regardless of its name.

How do you skip a component you don't need?

  • Use the keyword skip
  • Leave a blank space
  • Use null in that slot
  • Use an underscore _ in that position

Answer: Use an underscore _ in that position. An underscore _ discards the component in that position.

How does each Map entry destructure in a for loop?

  • Into (key, value)
  • Into (value, key)
  • Into a single Pair object only
  • It cannot be destructured

Answer: Into (key, value). for ((key, value) in map) unpacks each entry into key and value.

Which types support destructuring out of the box?

  • Only data classes
  • Only Pair
  • Pair, Triple, Map entries, and data classes
  • Every class automatically

Answer: Pair, Triple, Map entries, and data classes. Pair, Triple, Map entries, and data classes all provide componentN() automatically.

For 'data class User(val name: String, val age: Int)', what does 'val (age, name) = user' assign to your 'age' variable?

  • The user's name (component1)
  • The user's age
  • null
  • A compile error

Answer: The user's name (component1). Positional matching means your first variable gets component1(), which is name.

Can a regular (non-data) class with no componentN() be destructured?

  • Yes, always
  • No, not without componentN() operators
  • Only if it is open
  • Only inside a loop

Answer: No, not without componentN() operators. Without componentN() operators a plain class cannot be destructured.

What is the correct lambda syntax to destructure a Pair parameter?

  • forEach { name, score -> }
  • forEach { name; score -> }
  • forEach { [name, score] -> }
  • forEach { (name, score) -> }

Answer: forEach { (name, score) -> }. Destructured lambda parameters need parentheses: { (name, score) -> }.

What does a data class auto-generate to enable destructuring?

  • componentN() functions
  • An iterator
  • A toString only
  • A compareTo only

Answer: componentN() functions. Data classes auto-generate component1(), component2(), and so on.

Where is destructuring especially idiomatic?

  • Only in constructors
  • Iterating a Map with for ((k, v) in map)
  • Only in test code
  • Inside string templates

Answer: Iterating a Map with for ((k, v) in map). Looping a Map with for ((k, v) in map) is a classic, readable use of destructuring.

Continue this course

Frequently asked questions

How does destructuring actually work under the hood?

When you write val (a, b) = thing, Kotlin translates it into val a = thing.component1() and val b = thing.component2(). Any type that provides operator functions named component1(), component2(), and so on can be destructured. Pair, Triple, Map entries, and every data class supply these automatically, which is why they all work out of the box.

Is destructuring positional or by name?

It is strictly positional. The first name you write receives component1() — the first declared property — regardless of what you call it. So for data class User(val name: String, val age: Int), writing val (age, name) = user would put the user's name into your age variable. Always match the order of the class's properties, not the names.

How do I ignore a component I don't need?

Use an underscore in that position. In val (first, _, city) = person the middle component is computed but discarded, so you don't introduce an unused variable. The underscore is purely a placeholder — it tells the compiler 'skip this slot' and keeps your code clean when a class has more properties than you care about.

Where is destructuring most useful in real code?

Three places dominate. First, iterating a Map with for ((key, value) in map) reads far better than calling .key and .value. Second, returning multiple results from a function via Pair, Triple, or a small data class. Third, lambda parameters — collection functions like forEach and map let you destructure each element inline, as in list.map { (name, score) -> ... }.