Enums

Enum Syntax

Rust by example - Enums

Enums are custom data types that define a set of possible 'variants' or states that a value can be. A value of that enum type can only be one of those variants at any given time. Each variant can optionally hold associated data (fields) of different types like a struct or like a tuple:

/// An enum to represent different types of messages.
#[derive(Debug)]
enum Message {
    Quit,                       // Unit-like variant (no fields).
    Move { x: i32, y: i32 },    // Struct-like variant (named fields).
    Write(String),              // Tuple-like variant (numbered fields).
    ChangeColor(i32, i32, i32), // Another tuple-like variant.
}

/// Define methods on enums.
impl Message {
    fn call(&self) {
        // The `match` keyword allows us to compare a value against a series of
        // patterns. Each pattern can have associated code that will be
        // run if the value matches the pattern.
        match self {
            Message::Quit => println!("The Quit variant has no data to show."),
            Message::Move { x, y } => println!(
                "Move in the x direction {x} and in the y direction {y}"
            ),
            Message::Write(text) => println!("Message: {text}"),
            Message::ChangeColor(r, g, b) => {
                println!("Change the color to red {r}, green {g}, and blue {b}")
            }
        }
        // Enums make your code safer, because the compiler knows all the
        // possible variants a value can have, and makes sure the
        // `match` expression handles all possibilities.
    }
}

fn main() {
    // `msg` is assigned one of the variants.
    // Note the :: between the name of the type and the name of the variant.
    let msg = Message::Quit;
    println!("{msg:?}");
    // Or
    let msg = Message::Move { x: 10, y: 15 };
    println!("{msg:?}");
    // Or
    let msg = Message::ChangeColor(127, 0, 0);
    println!("{msg:?}");

    let msg = Message::Write(String::from("hello"));
    msg.call();
}

If we make an enum public, all of its variants are then public. We only need pub⮳ before the enum⮳ keyword.

Common enums

  • Option.
  • Result.

Convert between Strings and Enum Variants with strum

strum strum-crates.io strum-github strum-lib.rs cat-parsing cat-development-tools::procedural-macro-helpers

strum provides helpful macros for working with enums and strings. It also can convert from an integer to an enum, add custom properties to enum variants, etc.

//! `strum` provides derive macros that automatically implement various traits
//! for your enums, making it easy to convert them to and from strings, iterate
//! over their variants, and more.
//!
//! Add to your `Cargo.toml`:
//! ```toml
//! [dependencies]
//! strum = { version = "0.27", features = ["derive"] }
//! ```

use strum::Display;
use strum::EnumIter;
use strum::EnumString;
use strum::IntoEnumIterator; // Import the trait to use .iter().

// We derive a few `strum` traits in addition to standard `Debug` and
// `PartialEq`:
// - `Display` to easily print the enum variant as a string,
// - `EnumString` to parse a string into an enum variant,
// - `EnumIter` to iterate over all possible variants of the enum.
#[derive(Debug, Display, EnumString, EnumIter, PartialEq)]
enum Direction {
    #[strum(serialize = "N")] // Customize the `Display` representation.
    North,
    #[strum(serialize = "E")]
    East,
    #[strum(serialize = "S")]
    South,
    #[strum(serialize = "W")]
    West,
}

fn main() {
    println!("--- Displaying Enum Variants ---");
    let dir = Direction::North;
    // Using the `Display` trait.
    println!("Current direction: {}", dir); // Prints "Current direction: North".

    println!("\n--- Parsing Strings into Enums ---");
    let input_string_south = "South";
    let input_string_invalid = "Up";

    // Using the EnumString trait:
    match input_string_south.parse::<Direction>() {
        Ok(direction) => println!(
            "Parsed '{}' successfully: {:?}.",
            input_string_south, direction
        ),
        Err(e) => println!("Failed to parse '{}': {}.", input_string_south, e),
    }

    match input_string_invalid.parse::<Direction>() {
        Ok(direction) => println!(
            "Parsed '{}' successfully: {:?}.",
            input_string_invalid, direction
        ),
        Err(e) => {
            println!("Failed to parse '{}': {}.", input_string_invalid, e)
        }
    }

    println!("\n--- Iterating Over Enum Variants ---");
    // Using the `EnumIter` trait:
    println!("All possible directions:");
    for direction in Direction::iter() {
        println!("- {:?}", direction);
    }
    // This will print:
    // - North
    // - East
    // - South
    // - West
}

Related Topics

  • Match.
  • Rust Patterns.
  • Functional Programming.
  • Data Types.
  • Structs.