---
title: "11.5 Destructuring"
description: "Patterns aren't only for match: destructuring pulls apart structs, tuples, and enums, including in let bindings and function parameters, with nesting."
url: "https://learnrust.net/chapter-11/destructuring/"
last_updated: "2026-06-13"
---

# 11.5 Destructuring

The patterns from lesson [11.4](https://learnrust.net/chapter-11/match-in-depth/) do something worth naming on its own: they take a compound value apart into its pieces. That operation is called **destructuring**, and it isn't limited to `match`. It works in `let` bindings, in function parameters, and on every compound type you've met: tuples, structs, and enums. Once you see how broadly patterns apply, a lot of Rust syntax you've already used turns out to have been destructuring all along.

## You've destructured before

Back in lesson [4.11](https://learnrust.net/chapter-4/tuples-and-the-unit-type/) you wrote this to unpack a tuple:

```rust
let (a, b) = (1, 2);
```

That `(a, b)` on the left is a pattern. It destructures the tuple, binding `a` to the first element and `b` to the second. The same `let` that creates a variable can carry a pattern, and when it does, it pulls the value apart. You've been using a pattern in every tuple-returning call since chapter 4 without calling it one.

## Destructuring structs

A struct destructures by naming its fields in braces, which is the struct literal syntax run backwards:

```rust
struct Point {
    x: f64,
    y: f64,
}

fn main() {
    let p = Point { x: 3.0, y: 4.0 };
    let Point { x, y } = p;
    println!("x is {x}, y is {y}");
}
```

```
x is 3, y is 4
```

`let Point { x, y } = p;` binds `x` to `p.x` and `y` to `p.y` in one line. If you want different variable names, write `let Point { x: a, y: b } = p;` to bind `a` and `b`. And you can ignore fields you don't need: `let Point { x, .. } = p;` binds only `x` and the `..` discards the rest, the same `..` "and the rest" you met in struct update (lesson [10.3](https://learnrust.net/chapter-10/field-init-and-update-syntax/)), here on the receiving side.

This shines in `match` when you only care about some fields or want to branch on a field's value:

```rust
fn describe(p: &Point) -> &str {
    match p {
        Point { x: 0.0, y: 0.0 } => "origin",
        Point { x: 0.0, .. } => "on the y-axis",
        Point { y: 0.0, .. } => "on the x-axis",
        _ => "somewhere else",
    }
}
```

The first arm matches only the exact origin (literal `0.0` in both fields), the next two match points sitting on an axis, and the catch-all handles the rest. The pattern tests specific field values and ignores the others, all in the shape of the struct itself.

## Destructuring enums

You've been destructuring enums since lesson [11.2](https://learnrust.net/chapter-11/enums-with-data/): `Some(value)`, `Message::Move { x, y }`, `Coin::Quarter(state)` are all enum patterns that pull out carried data. The new idea here is just that the same patterns nest. An enum variant holding a struct, or a tuple holding an `Option`, comes apart in one pattern that mirrors the value's shape:

```rust
enum Shape {
    Circle { radius: f64 },
    Rectangle { width: f64, height: f64 },
}

fn area(shape: &Shape) -> f64 {
    match shape {
        Shape::Circle { radius } => 3.14159 * radius * radius,
        Shape::Rectangle { width, height } => width * height,
    }
}
```

`Shape::Circle { radius }` matches the variant *and* destructures its named field in one pattern. A pattern can be as deep as the value: `Some(Point { x, .. })` matches an `Option` containing a `Point` and binds that point's `x`, skipping the rest. The pattern always looks like the value it matches, which is what makes deeply nested data readable instead of a pile of `.0.1.field` accesses.

## Destructuring in function parameters

A function parameter is a binding, so it can be a pattern too. If a function always pulls a struct apart immediately, you can destructure right in the signature:

```rust
struct Point {
    x: f64,
    y: f64,
}

fn distance_from_origin(Point { x, y }: &Point) -> f64 {
    (x * x + y * y).sqrt()
}

fn main() {
    let p = Point { x: 3.0, y: 4.0 };
    println!("{}", distance_from_origin(&p));
}
```

```
5
```

The parameter `Point { x, y }: &Point` says "take a `&Point`, and immediately bind `x` and `y` to its fields." This is occasionally tidier than taking `p: &Point` and writing `p.x`, `p.y` inside. Use it sparingly; for most methods the `&self` from lesson [10.5](https://learnrust.net/chapter-10/methods-and-impl-blocks/) reads better, but it's good to recognize that the parameter slot accepts any pattern.

> **Key insight**
>
> Destructuring and construction are mirror images. `Point { x: 3.0, y: 4.0 }` builds a point from its parts; `let Point { x, y } = p;` takes one apart into its parts. The same syntax, read in two directions. Wherever Rust binds a name (a `let`, a function parameter, a `match` arm), it accepts a pattern, and a pattern can always disassemble a value whose shape it describes.

> **Warning**
>
> A `let` pattern must match *every* possible value of the type, or the compiler refuses it. Destructuring a struct or tuple in `let` is fine, because there's only one shape. Destructuring a *specific enum variant* in `let` is not: `let Some(x) = maybe;` is rejected, because `maybe` could be `None` and then there's nothing to bind. For that you need `match`, or the `if let` / `let else` tools in lesson [11.6](https://learnrust.net/chapter-11/if-let-while-let-let-else/), which exist precisely for the "this pattern might not match" case.

## Quiz time

**Question #1**

What does this print?

```rust
fn main() {
    let ((a, b), c) = ((1, 2), 3);
    println!("{a} {b} {c}");
}
```

<details class="solution">
<summary>Show solution</summary>

`1 2 3`. The pattern `((a, b), c)` mirrors the nested tuple: `a` and `b` come from the inner `(1, 2)`, and `c` from the outer `3`. Patterns nest as deeply as the value.

</details>

**Question #2**

Destructure this struct in a single `let`, binding the width to `w` and the height to `h` (note the renamed bindings), and print their product.

```rust
struct Rect { width: f64, height: f64 }
let r = Rect { width: 4.0, height: 2.0 };
```

<details class="solution">
<summary>Show solution</summary>

```rust
let Rect { width: w, height: h } = r;
println!("{}", w * h);   // 8
```

`width: w` binds the `width` field to a new name `w`; same for `h`. Without renaming, `let Rect { width, height } = r;` would bind variables named `width` and `height`.

</details>

**Question #3**

Why is `let Some(x) = get_value();` refused, while `let (a, b) = get_pair();` is fine?

<details class="solution">
<summary>Show solution</summary>

A `let` pattern must cover every possible value of the type. `get_pair()` returns a tuple, which has exactly one shape, so `(a, b)` always matches. `get_value()` returns an `Option`, which can be `None`, and `Some(x)` doesn't match `None`, leaving `x` unbound. A `let` can't have an "unmatched" case, so the compiler refuses it and points you toward `match`, `if let`, or `let else`.

</details>

That warning is the perfect setup for the next lesson, which introduces three tools for exactly the "a pattern that might not match" situation: `if let`, `while let`, and `let else`, each a lighter alternative to a full `match`.

## Sitemap

See the full [sitemap](https://learnrust.net/sitemap.md) for all pages.
