Enums with Data
Part of the Object Oriented Programming section of Coddy's Rust journey — lesson 14 of 61.
Basic enums in Rust let you define a type with a fixed set of variants—like Direction::North or Status::Active. But Rust enums are far more powerful than that. Each variant can carry its own data, making enums incredibly flexible for modeling real-world scenarios.
Consider a messaging system where different message types carry different information. A text message has content, a move command has coordinates, and a quit signal has nothing at all. In Rust, you can represent all of these with a single enum:
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(u8, u8, u8),
}
Each variant has a different shape. Quit holds no data—it's just a signal.
Move uses struct-like syntax with named fields. Write holds a single String using tuple syntax. ChangeColor holds three values representing RGB components.
Creating instances of these variants looks like this:
let m1 = Message::Quit;
let m2 = Message::Move { x: 10, y: 20 };
let m3 = Message::Write(String::from("Hello"));
let m4 = Message::ChangeColor(255, 128, 0);
The beauty of this approach is that m1, m2, m3, and m4 are all the same type: Message. You can store them in the same collection, pass them to the same function, or return any variant from a single function. The enum unifies different data shapes under one type while preserving the specific information each variant needs.
Challenge
EasyLet's build a notification system that uses enums with data to represent different types of alerts. Each notification type carries different information—some have messages, some have numeric values, and some are simple signals.
You'll create two files to organize your code:
notification.rs: Define a publicNotificationenum with three variants:Alert— holds a singleStringmessage (tuple syntax)Reminder— holds named fields:title(String) andminutes(u32)Dismiss— holds no data, just a signal
main.rs: Bring in your notification module and create one instance of each variant. Then print information about each notification to demonstrate that all three variants are the same type but carry different data.
When printing, use debug formatting ({:?}) to display each notification. You'll need to derive the Debug trait on your enum so it can be printed this way.
Your output should show all three notifications, each on its own line:
Alert("{message}")
Reminder { title: "{title}", minutes: {minutes} }
DismissFor example, with an alert message of "Server down", a reminder with title "Meeting" and 30 minutes, the output would be:
Alert("Server down")
Reminder { title: "Meeting", minutes: 30 }
DismissYou will receive three inputs: the alert message, the reminder title, and the reminder minutes.
Cheat sheet
Enum variants in Rust can carry different types of data, making them powerful for modeling diverse scenarios under a single type.
Defining Enums with Data
Each variant can have its own data structure:
enum Message {
Quit, // No data
Move { x: i32, y: i32 }, // Named fields (struct-like)
Write(String), // Single value (tuple syntax)
ChangeColor(u8, u8, u8), // Multiple values (tuple syntax)
}
Creating Enum Instances
let m1 = Message::Quit;
let m2 = Message::Move { x: 10, y: 20 };
let m3 = Message::Write(String::from("Hello"));
let m4 = Message::ChangeColor(255, 128, 0);
All instances are the same type (Message) despite carrying different data, allowing them to be stored in collections, passed to functions, or returned from functions uniformly.
Debug Trait
To print enums using {:?} formatting, derive the Debug trait:
#[derive(Debug)]
enum Message {
// variants...
}
Try it yourself
mod notification;
use notification::Notification;
fn main() {
// Read inputs
let mut alert_message = String::new();
std::io::stdin().read_line(&mut alert_message).expect("Failed to read line");
let alert_message = alert_message.trim().to_string();
let mut reminder_title = String::new();
std::io::stdin().read_line(&mut reminder_title).expect("Failed to read line");
let reminder_title = reminder_title.trim().to_string();
let mut minutes_input = String::new();
std::io::stdin().read_line(&mut minutes_input).expect("Failed to read line");
let reminder_minutes: u32 = minutes_input.trim().parse().expect("Failed to parse minutes");
// TODO: Create an Alert notification using the alert_message
// TODO: Create a Reminder notification using reminder_title and reminder_minutes
// TODO: Create a Dismiss notification
// TODO: Print each notification using debug formatting {:?}
// Each notification should be printed on its own line
}
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