Menu
Coddy logo textTech

Methods on Enums

Part of the Object Oriented Programming section of Coddy's Rust journey — lesson 15 of 61.

Just like structs, enums can have methods defined in impl blocks. This allows you to attach behavior directly to your enum type, making it easy to work with different variants through a unified interface.

Here's how to add a method to the Message enum from the previous lesson:

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
}

impl Message {
    fn describe(&self) -> String {
        match self {
            Message::Quit => String::from("Quit signal"),
            Message::Move { x, y } => format!("Move to ({}, {})", x, y),
            Message::Write(text) => format!("Message: {}", text),
        }
    }
}

The impl Message block works exactly like it does for structs. The method takes &self to access the enum instance. Inside the method, you use match to determine which variant you're dealing with and return the appropriate result.

Calling the method looks the same regardless of which variant you have:

fn main() {
    let m1 = Message::Quit;
    let m2 = Message::Move { x: 10, y: 20 };
    
    println!("{}", m1.describe());  // Quit signal
    println!("{}", m2.describe());  // Move to (10, 20)
}

This pattern is powerful because the caller doesn't need to know which variant they have—they just call the method, and the enum handles the rest internally. The behavior changes based on the variant, but the interface stays consistent.

challenge icon

Challenge

Easy

Let's build a traffic light system that uses an enum with methods to describe the current signal state. Each light color will carry information about how long it lasts, and a method will provide a human-readable description.

You'll create two files to organize your code:

  • traffic.rs: Define a public TrafficLight enum with three variants:
    • Red — holds a u32 representing seconds to wait
    • Yellow — holds a u32 representing seconds to wait
    • Green — holds a u32 representing seconds to go
    Then implement a status method that returns a String describing the light. Use match inside the method to handle each variant and include the duration in the message.
  • main.rs: Bring in your traffic module, create instances of each light variant, and call the status method on each to print the descriptions.

The status method should return messages in these exact formats:

  • For Red: Stop for {seconds} seconds
  • For Yellow: Caution for {seconds} seconds
  • For Green: Go for {seconds} seconds

Your output should display the status of each light on its own line:

Stop for {seconds} seconds
Caution for {seconds} seconds
Go for {seconds} seconds

For example, with a red light of 30 seconds, yellow of 5 seconds, and green of 25 seconds, the output would be:

Stop for 30 seconds
Caution for 5 seconds
Go for 25 seconds

You will receive three inputs: the red light duration, the yellow light duration, and the green light duration.

Cheat sheet

Enums can have methods defined in impl blocks, just like structs:

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
}

impl Message {
    fn describe(&self) -> String {
        match self {
            Message::Quit => String::from("Quit signal"),
            Message::Move { x, y } => format!("Move to ({}, {})", x, y),
            Message::Write(text) => format!("Message: {}", text),
        }
    }
}

Methods take &self to access the enum instance and use match to handle different variants:

let m1 = Message::Quit;
let m2 = Message::Move { x: 10, y: 20 };

println!("{}", m1.describe());  // Quit signal
println!("{}", m2.describe());  // Move to (10, 20)

The caller uses the same interface regardless of the variant—the enum handles the behavior internally based on which variant it is.

Try it yourself

mod traffic;

use traffic::TrafficLight;

fn main() {
    // Read input for durations
    let mut red_input = String::new();
    std::io::stdin().read_line(&mut red_input).expect("Failed to read line");
    let red_duration: u32 = red_input.trim().parse().expect("Invalid number");

    let mut yellow_input = String::new();
    std::io::stdin().read_line(&mut yellow_input).expect("Failed to read line");
    let yellow_duration: u32 = yellow_input.trim().parse().expect("Invalid number");

    let mut green_input = String::new();
    std::io::stdin().read_line(&mut green_input).expect("Failed to read line");
    let green_duration: u32 = green_input.trim().parse().expect("Invalid number");

    // TODO: Create instances of each TrafficLight variant with the input durations

    // TODO: Call the status method on each light and print the result
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming