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
EasyLet'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 publicTrafficLightenum with three variants:Red— holds au32representing seconds to waitYellow— holds au32representing seconds to waitGreen— holds au32representing seconds to go
statusmethod that returns aStringdescribing the light. Usematchinside 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 thestatusmethod 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} secondsFor 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 secondsYou 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
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Methods and Behavior
Intro Implementation BlocksThe Self ParameterMutable MethodsAssociated FunctionsMultiple Implementation BlocksMethod ChainingRecap - Rectangle Actions4Project: Virtual Pet
Defining the PetFeeding the Pet7Standard Traits
The Debug TraitThe Display TraitClone and CopyEquality TraitsRecap - Printable Point10Project: Document System
The Draw TraitText Component2Encapsulation and Modules
Modules BasicsThe Public KeywordPrivate FieldsGettersSettersRecap - Secure Locker5Generics
Generic StructsGeneric MethodsMultiple Generic TypesGeneric FunctionsRecap - Coordinate Point8Traits as Bounds
Trait Bounds SyntaxMultiple BoundsThe Where ClauseReturning Types with TraitsRecap - Generic Printer11Design Patterns in Rust
Newtype PatternCompositionThe Drop TraitFrom and IntoRecap - Smart Pointer Mock