Result

Handle Recoverable Errors with Result

std

Result<T, E> is an enum used for error handling. It has two variants:

  • Ok(T) represents success and contains a value of type T.
  • Err(E) represents an error and contains an error value of type E.

Functions should return Result when errors are expected and recoverable. Note that Result is annotated with the #[must_use] attribute: the compiler issues a warning when a Result value is ignored.

The following example show to return Result from a function, propagate it to the caller with the ? operator, or process it with match. The anyhow and thiserror libraries can be used to significantly simplify error handling.

//! `Result` is a type that represents either success (`Ok`) or failure (`Err`):
//!
//! ```
//! pub enum Result<T, E> {
//!     Ok(T),
//!     Err(E),
//! }
//! ```

use std::fs::File;
use std::io;
use std::io::Read;
use std::num::ParseIntError;

fn open_file(file_path: &str) {
    // Faillible functions like `File::open` return a `Result`...
    let open_result: Result<File, io::Error> = File::open(file_path);
    // You could handle their `Result` there and then...
    match open_result {
        Err(e) => eprintln!("An error occurred while opening the file: {}", e),
        Ok(file) => println!("Opened {:?}", file),
    }
}

// ...but most often you'll want to return early when encountering an error,
// and propagate the error up the call stack.
#[allow(clippy::question_mark)]
fn read_file(file_path: &str) -> Result<String, io::Error> {
    let mut file = match File::open(file_path) {
        Ok(file) => file,
        Err(e) => return Err(e),
    };
    // This said, having to use multiple `match` or `if let` is verbose
    // when chaining calls to faillible methods...
    let mut contents = String::new();
    if let Err(e) = file.read_to_string(&mut contents) {
        Err(e)
    } else {
        Ok(contents)
    }
}

// Therefore, use the `?` operator as a shortcut to return early
// in case of an error. The following is equivalent to the previous function.
fn read_file2(file_path: &str) -> Result<String, io::Error> {
    let mut file: File = File::open(file_path)?;
    // Note that `file` is of type `File`,
    // not `io::Result<File> = Result<File, io::Error>`
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
}
// You can even chain method calls immediately after the `?`, e.g.
// `File::open(file_path)?.read_to_string(&mut contents)?;`

// You will often need to return one of multiple `Result` types.
// You could create a custom error `enum` to do so:
fn read_and_parse_file(file_path: &str) -> Result<i32, MyError> {
    let mut file = File::open(file_path)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?; // `read_to_string` returns `Result<_, io::Error>`.
    let number = contents.trim().parse()?; // `parse` returns `Result<_, std::num::ParseIntError>`.
    Ok(number)
}

#[allow(dead_code)]
#[derive(Debug)]
enum MyError {
    Io(io::Error),
    Parse(ParseIntError),
}

// That custom error type must implement the `From` trait.
impl From<io::Error> for MyError {
    fn from(err: io::Error) -> MyError {
        MyError::Io(err)
    }
}
impl From<ParseIntError> for MyError {
    fn from(err: ParseIntError) -> MyError {
        MyError::Parse(err)
    }
}

// The `thisError` crate provides a convenient `derive` macro
// for the standard library's `std::error::Error` trait.
// Use when writing libraries, avoiding the need for custom error boilerplate.
#[allow(dead_code)]
#[derive(thiserror::Error, Debug)]
enum MyError2 {
    #[error("Io error")]
    Io(#[from] io::Error),
    #[error("Parse error")]
    Parse(#[from] ParseIntError),
}

// A simpler method than returning a custom error type
// may be to return a trait object (at the cost of opacity)...
fn read_and_parse_file2(
    file_path: &str,
) -> Result<i32, Box<dyn std::error::Error>> {
    let mut file = File::open(file_path)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    let number = contents.trim().parse()?;
    Ok(number)
}

// ...but crates like `anyhow` can simplify error management a great deal...
// `anyhow::Result<T>` is equivalent to `std::result::Result<T, anyhow::Error>`.
fn read_and_parse_file3(file_path: &str) -> anyhow::Result<i32> {
    let mut file = File::open(file_path)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    let number = contents.trim().parse()?;
    Ok(number)
}

// A function without return value in truth returns `Unit`:
// `fn func() {...}` is a shorthand for `fn func() -> () { ... }`
// To convert it to a faillible function, return `Result<(), SomeErrorType>`.
fn unit_return_value(file_path: &str) -> anyhow::Result<()> {
    let _i: i32 = read_and_parse_file3(file_path)?;
    println!("I don't return anything!");
    // Do not forget to return an Result in the happy path.
    // The double parentheses are required. It is `Ok( () )`.
    Ok(())
}

// `main()` can return a `Result`, more precisely a `Result<T, E> where T:
// Termination, E: Debug)`. `Result<(), Box<dyn std::error::Error>>`,
// `anyhow::Result<()>` are common choices.
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let file_path = "example.txt";
    open_file(file_path);
    // Here we ignore the `Result` return values
    // to avoid returning early and exercise all the code.
    // You should rarely need to do this.
    let _ = read_file(file_path);
    let _ = read_file2(file_path);
    let _ = read_and_parse_file(file_path);
    let _ = read_and_parse_file2(file_path);
    let _ = read_and_parse_file3(file_path);
    let _ = unit_return_value(file_path);
    Ok(())
}

Retrieve or Transform Values in Result

std

In addition to working with pattern matching, Result provides a wide variety of convenience methods, the most common being:

MethodDescriptionUse Case
unwrap()Returns the Ok value or panics if Err.Use when you're certain the result is Ok. See also expect.
unwrap_or(default)Returns the Ok value or a default if Err.Use when you want a fallback value.
unwrap_or_else(func)Returns the Ok value or calls a function to generate a fallback.Use when computing a fallback dynamically.
map(func)Applies a function to the Ok value.Use to transform the result, if successful.
map_err(func)Applies a function to the Err value.Use to transform the error.
and_then(func)Applies a function that returns Result, flattening the result.Use for chaining operations that may fail.
or_else(func)Calls a function to provide an alternative Result, if Err.Use to recover from errors dynamically.
map_or(default, func)Applies a function to the Ok value or returns a default if Err.Use when you need a fallback value.
map_or_else(err_func, ok_func)Applies a function to the Ok value or calls another function for Err.Use when you need dynamic error handling.
is_ok()Returns true if the result is Ok.Use to check success.
is_err()Returns true if the result is Err.Use to check failure.

The following example demonstrates a few of these methods:

//! Example of `Result` methods for value transformation and control flow.

// A contrived example of a function returning a `Result`:
fn divide(a: i32, b: i32) -> Result<i32, &'static str> {
    if b == 0 {
        Err("Cannot divide by zero")
    } else {
        Ok(a / b)
    }
}

#[allow(clippy::bind_instead_of_map)]
fn main() {
    let result = divide(10, 2)
        // Transform the inner value if `Ok`.
        .map(|x| x * 2)
        // Chain another `Result`-returning operation.
        .and_then(|x| Ok(x + 1));

    // Check result status.
    println!("Is result Ok? {}", result.is_ok());
    println!("Is result Err? {}", result.is_err());

    println!("Final value: {}", result.unwrap_or(-1)); // Provide a default value if `Err`.
    // You could also write: .or_else(|_| Ok(-1));

    // TODO Ok("").expect("failed to write message"); // Panic with a provided
    // custom message if `Err`.
}

Get Reference to Values Inside a Result

std

The following example demonstrates the usage of the as_ref(), as_deref(), and as_mut() methods on the Result type:

  • as_ref() converts &Result<T, E> into Result<&T, &E>, allowing access to references.
  • as_deref() converts &Result<T, E> into Result<&U, &E> where T: Deref<Target = U>, useful for converting Result<String, E> to Result<&str, E>.
  • as_mut() converts &mut Result<T, E> into Result<&mut T, &mut E>, allowing mutation of the inner value.

These methods are useful for working with references to the values inside a Result without consuming the Result itself.

//! Demonstrates the use of `as_ref`, `as_deref`, and `as_mut` methods on
//! `Result`.

fn get_message(success: bool) -> Result<String, &'static str> {
    if success {
        Ok(String::from("Rust"))
    } else {
        Err("Failed to get message")
    }
}

fn main() {
    let result: Result<String, &'static str> = get_message(true);

    // `as_ref` converts from `&Result<T, E>` to Result<&T, &E>`.
    let ref_result: Result<&String, &&str> = result.as_ref();
    println!("as_ref(): {:?}", ref_result);

    // Use `as_deref` to coerce the `Ok` variant of the original `Result` via
    // `Deref`. Here, `&String` is coerced to `&str`.
    let deref_result: Result<&str, &&str> = result.as_deref();
    println!("as_deref(): {:?}", deref_result);

    // Use `as_mut` to get a mutable reference:
    let mut result_mut = get_message(true);
    let mut_ref: Result<&mut String, &mut &str> = result_mut.as_mut();
    if let Ok(s) = mut_ref {
        s.push_str(" is awesome!");
    }
    println!("as_mut(): {:?}", result_mut);

    // There is also a `as_deref_mut` method.
}

Result vs Option

std

Option<T> represents an optional value. Use Option when a value might be missing but isn't necessarily an error (e.g., searching for an item in a list).

Result<T, E> represents the outcome of an operation that can succeed or fail. Use Result when an operation can fail and you need to handle errors explicitly (e.g., file I/O operations).

The following table lists common methods that convert Result<T, E> to Option:

| Method | Description | Use Case | | ok() | Converts Result<T, E> into Option<T>, discarding the error. | Use when you only care about the success value. | | err() | Converts Result<T, E> into Option<E>, discarding the success value. | Use when you only care about the error. |

See also the Option chapter.

Related Topics

  • Error Handling.
  • Error Customization.
  • Iterators.
  • Rust Patterns.

References