---
title: How to use strum crate in Rust enum derive macros
summary: Use the strum crate and strum_macros to automatically generate Display, Iter, and other helper methods for Rust enums.
lang: en
slug: how-to-use-strum-crate-in-rust-enum-derive-macros
published: 2026-04-17T12:05:42+00:00
updated: 2026-04-25T22:00:13+00:00
canonical: https://www.rustfaq.org/en/how-to-use-strum-crate-in-rust-enum-derive-macros/
---

# How to use strum crate in Rust enum derive macros



## When enums need to talk to strings

You build an enum for configuration modes. You need to print the mode name to a log file. You also need to parse a string from a config file back into the mode. You write a `match` statement for printing. You write another `match` for parsing. You add a new variant later. You update the print match. You forget the parse match. The code compiles. The app crashes at runtime when it encounters the new mode.

Enums are compile-time guarantees. Strings are runtime chaos. Manual conversions drift. The `strum` crate ties string representations directly to the enum definition. If you add a variant, the generated code updates automatically. You get iteration, parsing, and display without the boilerplate.

## The derive macro approach

`strum` provides derive macros that generate trait implementations for your enums. You annotate the enum with `#[derive(...)]`. The macro runs at compile time, inspects the variants, and generates the code. There is no runtime cost for the generation. The output is standard Rust code.

The crate covers the common patterns: converting to strings, parsing from strings, iterating over variants, and handling serialization formats. It also includes advanced features like generating discriminant enums for enums that carry data.

## Minimal example

Add `strum` to your dependencies. The crate re-exports the macros, so you only need one dependency.

```toml
[dependencies]
strum = "0.26"
```

Derive `Display` to print variants and `EnumIter` to loop over them.

```rust
use strum_macros::{Display, EnumIter};

#[derive(Display, EnumIter)]
enum Color {
    Red,
    Blue,
    Green,
}

fn main() {
    // Display generates the fmt::Display impl.
    // This prints "Red" without manual formatting.
    println!("{}", Color::Red);

    // EnumIter creates a static iterator over all variants.
    // The order matches the declaration order in the enum.
    for color in Color::iter() {
        println!("Found: {}", color);
    }
}
```

The `Display` trait lets you use `{}` in format strings. `EnumIter` provides a `iter()` method that yields each variant. You can validate input by checking if a string exists in the iterator.

Convention aside: use `strum_macros` only if you need a specific version mismatch for some reason. The `strum` crate re-exports all macros. The community standard is to depend on `strum` and import from `strum_macros` or `strum` directly. This keeps versions aligned and avoids subtle breakage.

Tie the string to the variant. Drift is the enemy.

## Realistic example: CLI commands

Command-line tools often parse subcommands from strings and display help text. `strum` handles both directions. `EnumString` enables parsing. `Display` handles output. `serialize_all` controls the string format.

```rust
use strum_macros::{Display, EnumString, EnumIter};

#[derive(Debug, Display, EnumString, EnumIter)]
#[strum(serialize_all = "kebab-case")]
enum Command {
    Start,
    Stop,
    StatusCheck,
    ListUsers,
}

fn main() {
    let input = "status-check";

    // EnumString implements FromStr.
    // parse() returns a Result, handling invalid input gracefully.
    let cmd: Command = match input.parse() {
        Ok(c) => c,
        Err(_) => {
            eprintln!("Unknown command: {}", input);
            return;
        }
    };

    println!("Executing: {:?}", cmd);

    // Generate help text by iterating variants.
    // serialize_all ensures the help text matches the input format.
    println!("Available commands:");
    for c in Command::iter() {
        println!("  - {}", c);
    }
}
```

The `#[strum(serialize_all = "kebab-case")]` attribute applies to all variants. `StatusCheck` becomes `status-check`. This saves manual `#[strum(serialize = "...")]` on every variant. Supported formats include `lowercase`, `UPPERCASE`, `PascalCase`, `camelCase`, `snake_case`, `kebab-case`, `SCREAMING_SNAKE_CASE`, and `SCREAMING-KEBAB-CASE`.

If you forget `EnumString` and try to parse, the compiler rejects the code with `E0277` (the trait bound `Command: FromStr` is not satisfied). The error points directly to the missing derive.

Use `serialize_all` to enforce consistency. Manual serialization attributes are maintenance debt.

## Advanced: Discriminants for enums with data

Enums often carry data. `Display` and `EnumString` struggle with fields because the string representation is ambiguous. `strum` solves this with `EnumDiscriminants`. It generates a new enum containing only the variant names, stripping the data. You can derive traits on the discriminant enum separately.

```rust
use strum_macros::{EnumDiscriminants, Display};

#[derive(EnumDiscriminants)]
#[strum_discriminants(derive(Display))]
enum Message {
    Hello { id: i32 },
    Goodbye { reason: String },
    Data { payload: Vec<u8> },
}

fn main() {
    let msg = Message::Hello { id: 42 };

    // Convert to the discriminant enum to inspect the variant type.
    let discriminant = MessageDiscriminants::from(&msg);

    // Display works on the discriminant, ignoring the data.
    println!("Message type: {}", discriminant);

    // Match on the discriminant for routing logic.
    match discriminant {
        MessageDiscriminants::Hello => println!("Handling hello"),
        MessageDiscriminants::Goodbye => println!("Handling goodbye"),
        MessageDiscriminants::Data => println!("Handling data"),
    }
}
```

The macro generates `MessageDiscriminants`. The `#[strum_discriminants(derive(Display))]` attribute applies derives to the generated enum. This pattern is useful for routing, logging, or serialization where you need the variant name but not the payload.

Convention aside: `EnumDiscriminants` is often combined with `EnumIter` on the discriminant to iterate over variant types. This lets you validate that a message type is supported before processing the payload.

Generate the discriminant. Match the shape, ignore the payload.

## Performance: AsRefStr for comparisons

`Display` produces a `String` via `to_string()`. This allocates memory. If you only need a string slice for comparisons or map keys, use `AsRefStr`. It returns `&str` without allocation.

```rust
use strum_macros::{AsRefStr, EnumString};

#[derive(AsRefStr, EnumString)]
enum Status {
    Active,
    Pending,
    Failed,
}

fn main() {
    let status = Status::Active;

    // AsRefStr provides as_ref() which returns &str.
    // No allocation occurs.
    let name: &str = status.as_ref();
    println!("Status: {}", name);

    // Use as_ref() for efficient comparisons.
    if status.as_ref() == "Active" {
        println!("System is running");
    }
}
```

`AsRefStr` is faster than `Display` for comparisons. It avoids heap allocation. Use it for `HashMap` keys, database lookups, or any hot path where you compare enum variants to strings.

Use `AsRefStr` for comparisons. Avoid the allocation.

## Pitfalls and errors

Enums with fields break simple string conversion. If you derive `Display` on an enum with data, `strum` requires explicit formatting.

```rust
use strum_macros::Display;

#[derive(Display)]
enum Result {
    #[strum(to_string = "Success")]
    Ok,
    #[strum(to_string = "Error: {0}")]
    Err(String),
}

fn main() {
    println!("{}", Result::Err("timeout".to_string()));
}
```

The `#[strum(to_string = "...")]` attribute defines the format. Without it, the macro fails to generate the implementation. You get a compiler error about missing methods.

Parsing returns a `Result`. Unwrapping panics on invalid input. Handle the error explicitly.

```rust
let input = "invalid";
let cmd: Command = input.parse().expect("Valid command required");
```

`expect` panics with a message. In production code, return the error or provide a default.

Version mismatches cause cryptic errors. `strum` and `strum_macros` must share the same major version. Since `strum` re-exports macros, depending only on `strum` prevents drift.

Keep `strum` and `strum_macros` versions locked together. Mismatched versions break the macro expansion silently or with cryptic errors.

## Decision matrix

Use `strum` when you need to convert enum variants to and from strings without writing manual match arms. Use `strum` when you need to iterate over all variants of an enum for validation or help text. Use `strum` when you want to enforce serialization formats like kebab-case or snake-case across an entire enum. Use `strum` when you need to generate a discriminant enum for variants that carry data. Reach for manual `match` when your enum variants carry data that requires complex formatting logic beyond simple string interpolation. Reach for `serde` when you are serializing to JSON or other data formats and need full ecosystem integration.

Pick the tool that matches the job. `strum` handles the string glue. `serde` handles the data interchange.

## Where to go next

- [How enums work](/en/how-enums-work/)
- [Derive macros explained](/en/derive-macros-explained/)
- [When to use serde](/en/when-to-use-serde/)