Rust Guide > Documentation > Collections > Result

Introduction

The Result class in Rust is an enum that represents either success (Ok) or failure (Err). It is commonly used for error handling and returning results from functions that can fail. The Result enum has two variants: Ok(T) for successful results and Err(E) for errors.

Using Result

The Result class can be used to handle operations that may fail, providing a way to propagate errors up the call stack. Here is a basic example:

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

fn main() {
    match divide(4, 2) {
        Ok(result) => println!("Result: {}", result),
        Err(e) => println!("Error: {}", e),
    }

    match divide(4, 0) {
        Ok(result) => println!("Result: {}", result),
        Err(e) => println!("Error: {}", e),
    }
}
// Output:
// Result: 2
// Error: Cannot divide by zero

Key Methods

Below are some of the key methods exposed by the Result class:

unwrap

Returns the contained Ok value or panics if the value is Err.

fn main() {
    let result: Result<i32, &str> = Ok(5);
    println!("Unwrapped value: {}", result.unwrap());

    // Uncommenting the following line will cause a panic
    // let error: Result<i32, &str> = Err("Error");
    // println!("Unwrapped value: {}", error.unwrap());
}
// Output: Unwrapped value: 5

unwrap_or

Returns the contained Ok value or a provided default if the value is Err.

fn main() {
    let result: Result<i32, &str> = Ok(5);
    let error: Result<i32, &str> = Err("Error");

    println!("Value or default: {}", result.unwrap_or(0));
    println!("Value or default: {}", error.unwrap_or(0));
}
// Output:
// Value or default: 5
// Value or default: 0

map

Applies a function to the contained Ok value (if any) and returns a Result with the result.

fn main() {
    let result: Result<i32, &str> = Ok(5);
    let new_result = result.map(|x| x * 2);

    println!("Mapped value: {:?}", new_result);
}
// Output: Mapped value: Ok(10)

and_then

Applies a function to the contained Ok value (if any) that returns a Result, and flattens the result.

fn main() {
    let result: Result<i32, &str> = Ok(5);
    let new_result = result.and_then(|x| Ok(x * 2));

    println!("Mapped and flattened value: {:?}", new_result);
}
// Output: Mapped and flattened value: Ok(10)

is_ok / is_err

Returns true if the Result is Ok / Err, respectively.

fn main() {
    let result: Result<i32, &str> = Ok(5);
    let error: Result<i32, &str> = Err("Error");

    println!("Is result ok? {}", result.is_ok());
    println!("Is error ok? {}", error.is_ok());
    println!("Is result err? {}", result.is_err());
    println!("Is error err? {}", error.is_err());
}
// Output:
// Is result ok? true
// Is error ok? false
// Is result err? false
// Is error err? true

Example Usage

Example 1: Basic Usage

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

fn main() {
    match divide(4, 2) {
        Ok(result) => println!("Result: {}", result),
        Err(e) => println!("Error: {}", e),
    }

    match divide(4, 0) {
        Ok(result) => println!("Result: {}", result),
        Err(e) => println!("Error: {}", e),
    }
}
// Output:
// Result: 2
// Error: Cannot divide by zero

Example 2: Unwrapping Values

fn main() {
    let result: Result<i32, &str> = Ok(5);
    println!("Unwrapped value: {}", result.unwrap());

    // Uncommenting the following line will cause a panic
    // let error: Result<i32, &str> = Err("Error");
    // println!("Unwrapped value: {}", error.unwrap());
}
// Output: Unwrapped value: 5

Example 3: Providing Default Values

fn main() {
    let result: Result<i32, &str> = Ok(5);
    let error: Result<i32, &str> = Err("Error");

    println!("Value or default: {}", result.unwrap_or(0));
    println!("Value or default: {}", error.unwrap_or(0));
}
// Output:
// Value or default: 5
// Value or default: 0

Example 4: Mapping Values

fn main() {
    let result: Result<i32, &str> = Ok(5);
    let new_result = result.map(|x| x * 2);

    println!("Mapped value: {:?}", new_result);
}
// Output: Mapped value: Ok(10)

Example 5: Chaining Result Operations

fn main() {
    let result: Result<i32, &str> = Ok(5);
    let new_result = result.and_then(|x| Ok(x * 2));

    println!("Mapped and flattened value: {:?}", new_result);
}
// Output: Mapped and flattened value: Ok(10)

Considerations

  • Using Result helps avoid the need for exceptions and makes error handling explicit.
  • Be careful with methods like unwrap that can cause a panic if the Result is Err.
  • Consider using methods like unwrap_or or unwrap_or_else to provide default values and avoid panics.
  • When chaining multiple operations that can fail, use and_then to handle errors gracefully.

See Also

  • Option - An enum that can contain either a value or None, useful for handling optional values.