[Rust] Option vs Result

发布时间 2023-05-24 14:06:21作者: Zhentiw

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:

  1. Option:

    • An Option represents an optional value: every Option is either Some and contains a value, or None, 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 the Option.

  2. 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 an Ok), and E is the type of the value to be returned in the failure case (inside an Err).

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.

  1. 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."),
    }
}
  1. 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.