Enums (enumerations) in Rust allow you to define a type that can have one of several possible values, called variants. Each variant can optionally hold data. Pattern matching is a powerful feature that lets you handle different enum variants in a concise and safe way.
Defining an Enum
You define an enum using the enum keyword, followed by the enum name and its variants.
enum IpAddrKind {
V4,
V6,
}
This defines an enum IpAddrKind with two variants: V4 and V6.
Enums with Data
Variants can hold data.
enum IpAddr {
V4(u8, u8, u8, u8), // IPv4 address with four octets
V6(String), // IPv6 address as a string
}
fn main() {
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
}
Using Enums
You can create instances of enum variants and use them in functions.
fn route(ip_kind: IpAddrKind) {
// Implementation here
}
fn main() {
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
route(four);
route(six);
}
Pattern Matching with match
The match expression is used to handle different enum variants. It’s exhaustive, meaning you must cover all possible cases.
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
fn main() {
let coin = Coin::Penny;
println!("Value: {}", value_in_cents(coin)); // Output: Value: 1
}
Matching with Data
You can destructure data from variants.
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::Move { x, y } => println!("Move to x: {}, y: {}", x, y),
Message::Write(text) => println!("Text message: {}", text),
Message::ChangeColor(r, g, b) => println!("Change color to red {}, green {}, blue {}", r, g, b),
}
}
fn main() {
let msg = Message::Move { x: 10, y: 20 };
process_message(msg);
}
Concise Control Flow with if let
if let allows you to handle only one pattern while ignoring others.
fn main() {
let some_u8_value = Some(0u8);
if let Some(3) = some_u8_value {
println!("three");
} else {
println!("not three"); // This will print
}
}
while let Conditional Loops
while let continues looping as long as a pattern matches.
fn main() {
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
println!("{}", top);
}
// Prints 3, 2, 1
}
The _ Placeholder
Use _ to match any value you don’t care about.
fn main() {
let some_value = Some(5);
match some_value {
Some(_) => println!("Got a value"),
None => println!("No value"),
}
}
Summary
Enums and pattern matching are core to Rust’s type system, enabling safe and expressive code. Enums define types with multiple variants, and match ensures all cases are handled. if let and while let provide concise alternatives for specific patterns.