---
title: "11.7 Methods on enums"
description: "impl works on enums just like structs; the worked example is a state machine, where each method matches on self to decide behavior per variant."
url: "https://learnrust.net/chapter-11/methods-on-enums/"
last_updated: "2026-06-13"
---

# 11.7 Methods on enums

Everything chapter 10 taught about `impl` blocks and methods applies to enums, unchanged. You write `impl MyEnum { ... }`, the methods take `&self` / `&mut self` / `self` exactly as before, and inside them you `match` on `self` to do the right thing for each variant. This lesson shows the pattern and then builds the example enums were made for: a state machine.

## impl on an enum

Define methods on an enum the same way as on a struct (lesson [10.5](https://learnrust.net/chapter-10/methods-and-impl-blocks/)):

```rust
#[derive(Debug)]
enum TrafficLight {
    Red,
    Yellow,
    Green,
}

impl TrafficLight {
    fn action(&self) -> &str {
        match self {
            TrafficLight::Red => "stop",
            TrafficLight::Yellow => "slow down",
            TrafficLight::Green => "go",
        }
    }
}

fn main() {
    let light = TrafficLight::Green;
    println!("{}", light.action());
}
```

```
go
```

The only new wrinkle is inside the method: `match self`. Because the method takes `&self`, the `self` in the `match` is a `&TrafficLight`, and the arms compare it against each variant. This `match self` shape is the bread and butter of enum methods, you'll write it constantly. The method groups the per-variant behavior with the type, so callers write the readable `light.action()` instead of a free function.

## Methods that return a new variant

A method can also produce a *new* value of the enum. The traffic light's "what comes next" is a natural fit, and it's where enums start to feel like machines:

```rust
impl TrafficLight {
    fn next(&self) -> TrafficLight {
        match self {
            TrafficLight::Red => TrafficLight::Green,
            TrafficLight::Green => TrafficLight::Yellow,
            TrafficLight::Yellow => TrafficLight::Red,
        }
    }
}

fn main() {
    let mut light = TrafficLight::Red;
    for _ in 0..4 {
        println!("{light:?} -> action: {}", light.action());
        light = light.next();
    }
}
```

```
Red -> action: stop
Green -> action: go
Yellow -> action: slow down
Red -> action: stop
```

`next` takes `&self`, reads the current variant, and returns the variant that follows. The loop in `main` reassigns `light` to its successor each pass, so the light cycles. This is a **state machine**: a value that is in exactly one of a fixed set of states, with defined transitions between them. Enums model state machines perfectly, because "exactly one of a fixed set" is precisely what an enum is, and `match` makes the transition table impossible to write incompletely. Forget a transition and the `match` in `next` won't compile (E0004, exhaustiveness from lesson [11.1](https://learnrust.net/chapter-11/enums/)).

## A state machine with data

The traffic light's states carry no data. Real state machines often do, and this is where data-carrying variants (lesson [11.2](https://learnrust.net/chapter-11/enums-with-data/)) and methods combine into something genuinely expressive. Model a simple traffic-light-controlled task, or here, a small download:

```rust
#[derive(Debug)]
enum Download {
    Idle,
    InProgress { percent: u8 },
    Done { bytes: u64 },
    Failed(String),
}

impl Download {
    fn status_line(&self) -> String {
        match self {
            Download::Idle => String::from("waiting to start"),
            Download::InProgress { percent } => format!("downloading: {percent}%"),
            Download::Done { bytes } => format!("complete: {bytes} bytes"),
            Download::Failed(reason) => format!("failed: {reason}"),
        }
    }

    fn is_finished(&self) -> bool {
        matches!(self, Download::Done { .. } | Download::Failed(_))
    }
}

fn main() {
    let states = [
        Download::Idle,
        Download::InProgress { percent: 47 },
        Download::Done { bytes: 1024 },
        Download::Failed(String::from("timeout")),
    ];

    for state in &states {
        println!("{} (finished: {})", state.status_line(), state.is_finished());
    }
}
```

```
waiting to start (finished: false)
downloading: 47% (finished: false)
complete: 1024 bytes (finished: true)
failed: timeout (finished: true)
```

Each variant carries the data its state needs: a percent while downloading, a byte count when done, a reason when failed, and nothing when idle. `status_line` matches and uses each variant's data; `is_finished` asks a yes/no question about the state, and counts both `Done` and `Failed` as finished, which is why the last two lines both read `true`. The new tool there is **`matches!`**, a macro that returns `true` if the value matches the pattern and `false` otherwise, a compact shorthand for `match x { pattern => true, _ => false }`. The `|` inside it is the multiple-pattern syntax from lesson [11.4](https://learnrust.net/chapter-11/match-in-depth/).

> **Key insight**
>
> An enum plus an `impl` block is a small machine: the variants are the states, data-carrying variants attach the data each state needs, and methods that `match self` are the operations and transitions. Because the enum lists every state and `match` must cover every variant, the compiler verifies your machine handles all its states, every time you change the set of states. This is "make impossible states unrepresentable" (lesson [3.6](https://learnrust.net/chapter-3/finding-issues-early/)) delivered in full.

## Quiz time

**Question #1**

Add a method `is_go(&self) -> bool` to `TrafficLight` that returns `true` only for `Green`. Write it two ways: with a `match`, and with `matches!`.

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

```rust
impl TrafficLight {
    fn is_go(&self) -> bool {
        match self {
            TrafficLight::Green => true,
            _ => false,
        }
    }
}
```

Or, more compactly:

```rust
impl TrafficLight {
    fn is_go(&self) -> bool {
        matches!(self, TrafficLight::Green)
    }
}
```

`matches!` is the idiomatic choice for a one-pattern yes/no test.

</details>

**Question #2**

Why does the `next` method's `match` need to be exhaustive, and what does that buy you if you later add a `FlashingRed` variant?

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

`match` must cover every variant (E0004 otherwise). If you add `FlashingRed`, every `match` on `TrafficLight` that doesn't handle it stops compiling, including `next` and `action`. The compiler hands you a checklist of exactly the places that need a transition or behavior for the new state, so you can't ship a half-updated state machine.

</details>

**Question #3**

Give the `Download` enum a method `percent_done(&self) -> u8` that returns the percent for `InProgress`, `100` for `Done`, and `0` for everything else.

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

```rust
impl Download {
    fn percent_done(&self) -> u8 {
        match self {
            Download::InProgress { percent } => *percent,
            Download::Done { .. } => 100,
            _ => 0,
        }
    }
}
```

The `InProgress` arm binds `percent` (a `&u8`, since `self` is borrowed) and `*percent` reads the value out, the dereference from lesson [9.6](https://learnrust.net/chapter-9/slices/). `Done { .. }` ignores the byte count, and the `_` covers `Idle` and `Failed`.

</details>

That's the full enum toolkit: variants, carried data, `Option`, the depths of `match`, destructuring, the `if let` family, and methods. The chapter summary and quiz are next, and then chapter 12 takes the `Result` type that's been lurking in `parse` and `read_line` since chapter 1 and makes error handling something you do on purpose.

## Sitemap

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