Digest: option and result
This digest introduces two of the most important enums in Rust: Option
and Result
. They are the foundation of safe error handling and absence
handling. Understanding how to use them is essential for writing robust
Rust programs.
1. The option type
Option<T>
represents a value that may or may not be present.
#![allow(unused)] fn main() { let maybe_num: Option<i32> = Some(42); let nothing: Option<i32> = None; }
Extracting values
- unwrap: take the value, panic if
None
.
#![allow(unused)] fn main() { println!("{}", maybe_num.unwrap()); // 42 println!("{}", nothing.unwrap()); // panics }
- expect: like
unwrap
but with a custom panic message.
#![allow(unused)] fn main() { println!("{}", maybe_num.expect("Should have a value")); }
- if let: execute code only when the option is
Some
.
#![allow(unused)] fn main() { if let Some(x) = maybe_num { println!("Got {}", x); } }
- match: handle both cases explicitly.
#![allow(unused)] fn main() { match maybe_num { Some(x) => println!("Got {}", x), None => println!("Got nothing"), } }
- unwrap_or / unwrap_or_else: provide a fallback value.
#![allow(unused)] fn main() { let x = nothing.unwrap_or(0); // x = 0 }
2. The result type
Result<T, E>
represents success (Ok(T)
) or error (Err(E)
).
#![allow(unused)] fn main() { fn divide(a: i32, b: i32) -> Result<i32, &'static str> { if b == 0 { Err("cannot divide by zero") } else { Ok(a / b) } } }
Extracting values
- unwrap / expect: panic if
Err
.
#![allow(unused)] fn main() { println!("{}", divide(4, 2).unwrap()); // 2 println!("{}", divide(4, 0).unwrap()); // panics }
- if let: handle only the success case.
#![allow(unused)] fn main() { if let Ok(val) = divide(4, 2) { println!("Result is {}", val); } }
- match: handle both success and error.
#![allow(unused)] fn main() { match divide(4, 0) { Ok(val) => println!("Result is {}", val), Err(e) => println!("Error: {}", e), } }
- unwrap_or / unwrap_or_else: provide a default on error.
#![allow(unused)] fn main() { let val = divide(4, 0).unwrap_or(-1); // val = -1 }
3. The question mark operator
The ?
operator is the idiomatic way to propagate errors or None
values. It works only in functions returning Option
or Result
.
#![allow(unused)] fn main() { fn read_number() -> Result<i32, std::num::ParseIntError> { let text = "42"; let num: i32 = text.parse()?; // if parsing fails, return the error immediately Ok(num) } }
- With
Option
,None
will be returned early. - With
Result
,Err(e)
will be returned early.
4. Rules of thumb
- Use
unwrap
orexpect
only in tests, quick prototypes, or when failure is impossible. - Use
if let
when you only care about the success case. - Use
match
when you want to handle both success and failure explicitly. - Use
unwrap_or
when a fallback value makes sense. - Use
?
to simplify error propagation in functions that returnOption
orResult
.
5. Putting it together
fn safe_divide(a: i32, b: i32) -> Option<i32> { if b == 0 { None } else { Some(a / b) } } fn main() -> Result<(), Box<dyn std::error::Error>> { // Option with match match safe_divide(4, 2) { Some(val) => println!("4/2 = {}", val), None => println!("division by zero"), } // Option with if let if let Some(val) = safe_divide(10, 5) { println!("10/5 = {}", val); } // Result with ? let number: i32 = "123".parse()?; // parse returns Result println!("Parsed: {}", number); Ok(()) }
This example illustrates how Option
and Result
can be used together
in practice.