Nothing stops you from reaching for .unwrap() instead of pattern-matching an Option — it compiles fine, and it's one method call shorter than a match. It also throws away exactly the safety the page praises Option for: calling it on a None panics immediately, at the exact line, with no compiler warning beforehand that this path could fail.
let name = find_user(99).unwrap();
println!("Found: {}", name);
// thread 'main' panicked at src/main.rs:10:30:
// called `Option::unwrap()` on a `None` valueAn enum defines a type with a fixed set of named variants, and unlike C-style enums, each Rust variant can carry its own associated data of any shape. match compares a value against a series of patterns and runs the code for the first match; critically, the compiler requires every possible variant to be handled (or an explicit _ catch-all), so a newly added enum variant that isn't handled somewhere is a compile error, not a silently-skipped runtime bug.
Enums with data per variant
Each variant can carry different data — a fundamentally more expressive tool than C's plain-integer enums.
enum Message {
Quit, // no data
Move { x: i32, y: i32 }, // named fields, like a struct
Write(String), // one value
ChangeColor(i32, i32, i32), // a tuple of values
}
let msg = Message::Move { x: 10, y: 20 };Option<T>: Rust's answer to null
Rust has no null value. Instead, the standard library defines Option<T> as an ordinary enum with two variants: Some(T) holds a value, None holds nothing. Because it's a distinct type from T, the compiler forces you to explicitly handle the "nothing" case before you can use the inner value — there's no way to accidentally dereference a null pointer.
// The actual standard library definition:
// enum Option<T> { Some(T), None }
fn find_user(id: u32) -> Option<String> {
if id == 1 {
Some(String::from("Alice"))
} else {
None
}
}
match find_user(1) {
Some(name) => println!("Found: {}", name),
None => println!("Not found"),
}The match above is exactly what the bug's .unwrap() skips — matching handles the None case explicitly instead of assuming the value is always there. The fix is to go back to handling both cases, or to supply a fallback value rather than calling .unwrap() on an Option that can genuinely be empty:
let name = match find_user(99) {
Some(n) => n,
None => String::from("Unknown"),
};
println!("Found: {}", name);
// Found: Unknownmatch must be exhaustive; if let for the simple case
If a match doesn't cover every variant, it's a compile error — this is what makes match a safety feature, not just a switch statement. For the common case of only caring about one variant, if let is a shorter alternative to a full match.
let config_value: Option<u8> = Some(3);
// full match: handles both cases
match config_value {
Some(v) => println!("value is {}", v),
None => println!("no value"),
}
// if let: shorter, only cares about the Some case
if let Some(v) = config_value {
println!("value is {}", v);
}