10.4 Using Enums in Code
10.4.1 Pattern Matching with Enums
Pattern matching involves comparing a value against a pattern and, if it matches, binding variables to the data within the value. Matching in Rust is done from top to bottom, and the first pattern that matches is selected.
Example: Handling Messages
enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), } fn process_message(msg: Message) { match msg { Message::Quit => println!("Quit message"), Message::Move { x: 0, y: 0 } => println!("Not moving at all"), Message::Move { x, y } => println!("Move to x: {}, y: {}", x, y), Message::Write(text) => println!("Write message: {}", text), Message::ChangeColor(r, g, b) => { println!("Change color to red: {}, green: {}, blue: {}", r, g, b) } } } fn main() { let msg = Message::Move { x: 0, y: 0 }; process_message(msg); }
If the value matches a pattern, the code to the right of the =>
operator is executed. The code can use any bound variables. When the code contains more than a single statement, it must be enclosed in {}
. The different branches of the match
construct are separated by commas.
- Destructuring with Values: We can match specific values within the data, such as
x: 0, y: 0
. - Order Matters: Since matching is top-down, the
Message::Move { x: 0, y: 0 }
pattern will catch moves wherex
andy
are zero. - Default Cases: Patterns without specific values match any variant of that type.
We will discuss pattern matching in more detail in a later chapter.
10.4.2 The if let
Syntax
The if let
construct in Rust provides a concise and readable way to perform pattern matching when you're interested in a single pattern and want to execute code only if a value matches that pattern.
Example Using match
:
enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), } fn main() { let msg = Message::Write(String::from("Hello")); match msg { Message::Write(text) => println!("Message is: {}", text), _ => println!("Message is not a Write variant"), } }
Equivalent Using if let
:
enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), } fn main() { let msg = Message::Write(String::from("Hello")); if let Message::Write(text) = msg { println!("Message is: {}", text); } else { println!("Message is not a Write variant"); } }
The if let
construct allows you to combine pattern matching with conditional logic succinctly. It tests whether a value matches a specific pattern, and if it does, it executes the code within the if
block, binding any variables in the pattern to the corresponding parts of the value. This is particularly useful when you only care about one particular pattern and don't need to handle other patterns exhaustively.
- Simplifies Code: Avoids the need for a full
match
when only one pattern is of interest.
While the if let
construct can be chained with else if
for multiple patterns, it is typically used with a single if
condition.
Example with else if
:
enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), } fn main() { let msg = Message::Move { x: 0, y: 0 }; if let Message::Write(text) = msg { println!("Message is: {}", text); } else if let Message::Move { x: 0, y: 0 } = msg { println!("Not moving at all"); } else { println!("Message is something else"); } }
10.4.3 Methods on Enums
You can define methods on enums using the impl
block.
Example:
enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), } impl Message { fn call(&self) { match self { Message::Quit => println!("Quit message"), Message::Move { x: 0, y: 0 } => println!("Not moving at all"), Message::Move { x, y } => println!("Move to x: {}, y: {}", x, y), Message::Write(text) => println!("Write message: {}", text), Message::ChangeColor(r, g, b) => { println!("Change color to red: {}, green: {}, blue: {}", r, g, b) } } } } fn main() { let msg = Message::Move { x: 0, y: 0 }; msg.call(); }
- Encapsulation: Methods allow you to encapsulate behavior related to the enum.
- Pattern Matching Inside Methods: You can use
match
within methods to handle different variants.