🎯 Rust Enums: The Magic Menu of Choices
Imagine you have a magical menu at a restaurant. Instead of just “food”, you can pick exactly what type: Pizza, Burger, or Salad. That’s what enums do in Rust—they let you say “it’s one of THESE specific things!”
🍕 What is an Enum?
Think of an enum like a box of crayons. You know exactly which colors are inside—Red, Blue, Green. You can’t suddenly have “Purple” unless you add it to the box!
In Rust, an enum is a type that can be one of several specific values called variants.
enum TrafficLight {
Red,
Yellow,
Green,
}
// Using it:
let light = TrafficLight::Red;
Real-life examples:
- 🚦 Traffic light: Red, Yellow, or Green
- 🎮 Game direction: Up, Down, Left, Right
- 📱 Phone status: Ringing, Silent, Vibrate
🎨 Enum Variants: The Different Flavors
Each choice inside an enum is called a variant. Think of variants like different flavors of ice cream in your favorite shop.
enum IceCream {
Chocolate, // Variant 1
Vanilla, // Variant 2
Strawberry, // Variant 3
}
fn main() {
let my_choice = IceCream::Chocolate;
// Match to find which flavor!
match my_choice {
IceCream::Chocolate => println!("Yum! 🍫"),
IceCream::Vanilla => println!("Classic! 🍦"),
IceCream::Strawberry => println!("Fruity! 🍓"),
}
}
Key Point: Use EnumName::VariantName to pick a specific variant.
graph TD A["IceCream Enum"] --> B["Chocolate"] A --> C["Vanilla"] A --> D["Strawberry"]
📦 Enums with Data: Variants That Carry Gifts!
Here’s where it gets exciting! Variants can carry extra information inside them—like a gift box that contains something special!
Three Types of Data Variants:
1. Tuple-like (unnamed data):
enum Message {
Move { x: i32, y: i32 }, // Named fields
Write(String), // Tuple with String
ChangeColor(u8, u8, u8), // RGB values
Quit, // No data
}
let msg = Message::Write(String::from("Hello!"));
let color = Message::ChangeColor(255, 0, 128);
2. Struct-like (named fields):
enum Shape {
Circle { radius: f64 },
Rectangle { width: f64, height: f64 },
}
let my_shape = Shape::Circle { radius: 5.0 };
Extracting the data:
match my_shape {
Shape::Circle { radius } => {
println!("Circle with radius: {}", radius);
}
Shape::Rectangle { width, height } => {
println!("Rectangle: {}x{}", width, height);
}
}
Think of it like this:
- 📧 An envelope (variant) can be empty OR contain a letter (data)
- 📱 A notification can be “Silent” OR “Alarm(time)” with specific time!
🎁 The Option Enum: Maybe There, Maybe Not!
The Problem: What if something might not exist? Like looking for your friend in a crowd—they might be there, or they might not!
Rust solves this with the Option enum:
enum Option<T> {
Some(T), // Something is here!
None, // Nothing here!
}
Real example:
fn find_even(num: i32) -> Option<i32> {
if num % 2 == 0 {
Some(num) // Found an even number!
} else {
None // Not even, nothing to return
}
}
fn main() {
let result = find_even(4);
match result {
Some(n) => println!("Found even: {}", n),
None => println!("Not even!"),
}
}
graph TD A["Option"] --> B["Some#40;value#41; ✅"] A --> C["None ❌"] B --> D["The value exists!"] C --> E["Nothing here!"]
Why is this amazing?
- 🛡️ Forces you to handle “nothing” cases
- 🚫 No more “null pointer” crashes!
- ✅ The compiler helps you remember
🧰 Option Combinators: Magic Tools for Options
Instead of always using match, Rust gives you helper methods (combinators) to work with Options easily!
1. unwrap() - Brave but Dangerous!
let x: Option<i32> = Some(5);
let value = x.unwrap(); // Gets 5
// ⚠️ CRASHES if x is None!
2. unwrap_or() - Safe Default
let x: Option<i32> = None;
let value = x.unwrap_or(0); // Gets 0 (default)
3. map() - Transform the Inside
let x: Option<i32> = Some(5);
let doubled = x.map(|n| n * 2); // Some(10)
4. and_then() - Chain Operations
fn double_if_positive(n: i32) -> Option<i32> {
if n > 0 { Some(n * 2) } else { None }
}
let x: Option<i32> = Some(5);
let result = x.and_then(double_if_positive);
// Some(10)
5. is_some() & is_none() - Quick Checks
let x: Option<i32> = Some(5);
if x.is_some() {
println!("We have a value!");
}
Quick Reference Table:
| Method | What it does | Returns |
|---|---|---|
unwrap() |
Get value (crashes if None) | T |
unwrap_or(default) |
Get value or default | T |
map(fn) |
Transform inner value | Option |
and_then(fn) |
Chain Option-returning fn | Option |
is_some() |
Check if Some | bool |
is_none() |
Check if None | bool |
⚡ The Result Enum: Success or Error!
The Problem: Sometimes things can go wrong! Reading a file might fail. Dividing might hit zero. How do we handle this?
Rust uses the Result enum:
enum Result<T, E> {
Ok(T), // Success! Here's your value
Err(E), // Oops! Here's the error
}
Real example:
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err(String::from("Cannot divide by zero!"))
} else {
Ok(a / b)
}
}
fn main() {
match divide(10, 2) {
Ok(result) => println!("Answer: {}", result),
Err(e) => println!("Error: {}", e),
}
match divide(10, 0) {
Ok(result) => println!("Answer: {}", result),
Err(e) => println!("Error: {}", e),
}
}
graph TD A["Result"] --> B["Ok#40;value#41; ✅"] A --> C["Err#40;error#41; ❌"] B --> D["Operation succeeded!"] C --> E["Something went wrong!"]
Result also has Combinators!
// Safe unwrapping
let result: Result<i32, &str> = Ok(10);
let value = result.unwrap_or(0);
// Transform success value
let doubled = result.map(|n| n * 2); // Ok(20)
// Check status
if result.is_ok() {
println!("Success!");
}
The ? Operator: Error Shortcut!
Instead of writing match everywhere, use ? to automatically return errors:
fn read_and_double(input: &str) -> Result<i32, String> {
let num: i32 = input.parse()
.map_err(|_| String::from("Not a number!"))?;
Ok(num * 2)
}
The ? means: “If this is an error, return it immediately. Otherwise, continue with the Ok value.”
🎓 Option vs Result: When to Use Which?
| Scenario | Use This | Example |
|---|---|---|
| Value might not exist | Option |
Finding item in list |
| Operation might fail | Result |
Reading a file |
| “Nothing” is normal | Option |
Optional user bio |
| “Nothing” is an error | Result |
Parsing user input |
Memory trick:
- Option = “Maybe there’s something?” 🎁❓
- Result = “Did it work or break?” ✅❌
🚀 You’re Now an Enum Expert!
You learned:
- ✅ Enums = A type with specific variants
- ✅ Variants = The different choices in an enum
- ✅ Enums with Data = Variants carrying extra info
- ✅ Option = Some or None for optional values
- ✅ Option Combinators = Helper methods like map, unwrap_or
- ✅ Result = Ok or Err for operations that might fail
Enums are one of Rust’s superpowers! They help you write safer, cleaner code by making all possibilities explicit. No more hidden surprises! 🎉
Next up: Use these enums in real projects and watch your code become bulletproof! 🛡️
