Matching Data Variants
Part of the Object Oriented Programming section of Coddy's Rust journey — lesson 16 of 61.
In the previous lesson, you used match inside enum methods to handle different variants. Now let's focus on how match actually extracts the data stored inside those variants—a technique called destructuring.
When you match on an enum variant that holds data, you can bind that data to variables right in the pattern:
enum Event {
Click { x: i32, y: i32 },
KeyPress(char),
Resize(u32, u32),
}
fn handle_event(event: Event) {
match event {
Event::Click { x, y } => {
println!("Clicked at ({}, {})", x, y);
}
Event::KeyPress(key) => {
println!("Key pressed: {}", key);
}
Event::Resize(width, height) => {
println!("Resized to {}x{}", width, height);
}
}
}
Each arm extracts data differently based on the variant's structure. For struct-like variants, you use { field_name } to pull out named fields. For tuple variants, you use positional variables like (width, height).
The variable names you choose become available inside that match arm. This is powerful because you're simultaneously checking which variant you have and gaining access to its payload.
The compiler ensures you handle every variant, and the extracted values are ready to use immediately—no extra steps needed.
Challenge
EasyLet's build a command processor that handles different types of user commands. Each command carries different data, and your processor will use pattern matching to extract that data and produce appropriate responses.
You'll create two files to organize your code:
command.rs: Define a publicCommandenum with three variants:Say— holds a singleStringmessage (tuple syntax)Move— holds named fields:direction(String) andsteps(u32)Calculate— holds twoi32values representing numbers to add (tuple syntax)
executemethod that usesmatchto destructure each variant and return aStringdescribing the action taken.main.rs: Bring in your command module, create one instance of each command variant using the provided inputs, and call theexecutemethod on each to print the results.
Your execute method should destructure each variant to access its data and return messages in these exact formats:
- For
Say:Saying: {message} - For
Move:Moving {steps} steps {direction} - For
Calculate:{a} + {b} = {sum}
Your output should display the result of each command on its own line:
Saying: {message}
Moving {steps} steps {direction}
{a} + {b} = {sum}For example, with a say message of "Hello world", a move of 5 steps "north", and a calculation of 10 and 25, the output would be:
Saying: Hello world
Moving 5 steps north
10 + 25 = 35You will receive five inputs: the say message, the direction, the number of steps, and the two numbers to calculate.
Cheat sheet
Pattern matching with match allows you to destructure enum variants and extract their data directly in the pattern.
For tuple variants, use positional variables:
enum Event {
KeyPress(char),
Resize(u32, u32),
}
match event {
Event::KeyPress(key) => {
println!("Key: {}", key);
}
Event::Resize(width, height) => {
println!("{}x{}", width, height);
}
}For struct-like variants, use named field syntax:
enum Event {
Click { x: i32, y: i32 },
}
match event {
Event::Click { x, y } => {
println!("Clicked at ({}, {})", x, y);
}
}The extracted variables become available within their respective match arm, allowing you to check the variant type and access its data simultaneously.
Try it yourself
mod command;
use command::Command;
fn main() {
// Read inputs
let mut say_message = String::new();
std::io::stdin().read_line(&mut say_message).expect("Failed to read line");
let say_message = say_message.trim().to_string();
let mut direction = String::new();
std::io::stdin().read_line(&mut direction).expect("Failed to read line");
let direction = direction.trim().to_string();
let mut steps_input = String::new();
std::io::stdin().read_line(&mut steps_input).expect("Failed to read line");
let steps: u32 = steps_input.trim().parse().expect("Failed to parse steps");
let mut num1_input = String::new();
std::io::stdin().read_line(&mut num1_input).expect("Failed to read line");
let num1: i32 = num1_input.trim().parse().expect("Failed to parse num1");
let mut num2_input = String::new();
std::io::stdin().read_line(&mut num2_input).expect("Failed to read line");
let num2: i32 = num2_input.trim().parse().expect("Failed to parse num2");
// TODO: Create a Say command using say_message and execute it
// TODO: Create a Move command using direction and steps, and execute it
// TODO: Create a Calculate command using num1 and num2, and execute it
// TODO: Print the results of each command execution
}
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 Mock3Advanced Enums
Enums with DataMethods on EnumsMatching Data VariantsThe Option Enum RevisitedRecap - Shape Enum