[Rust] Option vs Result
Option
and Result
are two very central enums in Rust, and they are used for error handling and for representing the absence of a value. Here is a breakdown of both:
-
Option
:- An
Option
represents an optional value: everyOption
is eitherSome
and contains a value, orNone
, and does not. Option
is typically used when a function may succeed or fail, and you are only interested in the success case and don't care why the function failed.Option
is defined as follows:
enum Option<T> { Some(T), None, }
Here
T
is the type of the value to be stored in theOption
. - An
-
Result
:- A
Result
is a type that represents either success (Ok
) or failure (Err
). - Unlike
Option
,Result
does not assume failure is an "expected" or "uninteresting" case: it allows you to handle failures as well as successes. Result
is typically used when a function may succeed or fail, and you want to handle both cases.Result
is defined as follows:
enum Result<T, E> { Ok(T), Err(E), }
Here
T
is the type of the value to be returned in the success case (inside anOk
), andE
is the type of the value to be returned in the failure case (inside anErr
). - A
In general, you should use Result
when you want to understand what the error was, and you should use Option
when you don't care what the error was, only whether or not it occurred.
Some examples.
Option<T>
:
Suppose we have a function that finds a person's age based on their name in a predefined list. If the person is not in the list, it will return None
.
use std::collections::HashMap;
fn find_age(people: &HashMap<String, u8>, name: &str) -> Option<u8> {
people.get(name).cloned()
}
fn main() {
let mut people = HashMap::new();
people.insert("Alice".to_string(), 20);
people.insert("Bob".to_string(), 30);
match find_age(&people, "Alice") {
Some(age) => println!("Alice is {} years old.", age),
None => println!("I don't know Alice's age."),
}
match find_age(&people, "Charlie") {
Some(age) => println!("Charlie is {} years old.", age),
None => println!("I don't know Charlie's age."),
}
}
Result<T, E>
:
Suppose we have a function that tries to divide two numbers. If the divisor is zero, it will return an error.
fn divide(numerator: f64, denominator: f64) -> Result<f64, &'static str> {
if denominator == 0.0 {
Err("Cannot divide by zero")
} else {
Ok(numerator / denominator)
}
}
fn main() {
match divide(10.0, 2.0) {
Ok(result) => println!("10 divided by 2 is {}.", result),
Err(err) => println!("Error: {}", err),
}
match divide(10.0, 0.0) {
Ok(result) => println!("10 divided by 0 is {}.", result),
Err(err) => println!("Error: {}", err),
}
}
In the Option
example, we only care about whether we found the age or not. In the Result
example, we care about why the division failed, so we use a Result
to get an error message in case of failure.