Result Type

Using the Result<T, E> enum for error handling.

Intermediate⏱️ 35 min📚 Prerequisites: 1

Result Type

The Result<T, E> is an enum used for error handling. It has two variants: Ok(T) (success) and Err(E) (error).

Result Definition

RUST
enum Result<T, E> {
    Ok(T),
    Err(E),
}

Basic Usage

RUST
use std::fs::File;

let f = File::open("hello.txt");

match f {
    Ok(file) => println!("File opened"),
    Err(error) => println!("Error: {:?}", error),
}

unwrap() and expect()

RUST
let f = File::open("hello.txt").unwrap();  // Panic if error
let f = File::open("hello.txt").expect("Failed to open");

unwrap_or_else()

RUST
let f = File::open("hello.txt").unwrap_or_else(|error| {
    panic!("File opening error: {:?}", error);
});

Propagating Errors (?) Operator

RUST
fn read_username_from_file() -> Result<String, io::Error> {
    let mut f = File::open("hello.txt")?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

The ? operator automatically propagates the error if Err, or unwraps the Ok value.

Code Examples

Basic Result

Using Result

RUST
fn divide(numerator: f64, denominator: f64) -> Result<f64, String> {
    if denominator == 0.0 {
        Err(String::from("Division by zero!"))
    } else {
        Ok(numerator / denominator)
    }
}
fn main() {
    match divide(10.0, 2.0) {
        Ok(result) => println!("Result: {}", result),
        Err(e) => println!("Error: {}", e),
    }
    match divide(10.0, 0.0) {
        Ok(result) => println!("Result: {}", result),
        Err(e) => println!("Error: {}", e),
    }
}

Explanation:

Result allows us to explicitly handle errors. Ok contains the successful result, Err contains the error.

Unwrap Methods

Extracting Result values

RUST
fn safe_divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err(String::from("Division by zero"))
    } else {
        Ok(a / b)
    }
}
fn main() {
    let result1 = safe_divide(10.0, 2.0).unwrap_or(0.0);
    println!("Result: {}", result1);
    let result2 = safe_divide(10.0, 2.0).expect("Division failed");
    println!("Result: {}", result2);
}

Explanation:

unwrap_or safely returns a value, or the default if there's an error. expect panics if there's an error.

? Operator

Propagating errors with the ? operator

RUST
fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        return Err(String::from("Division by zero"));
    }
    Ok(a / b)
}
fn calculate(a: f64, b: f64, c: f64) -> Result<f64, String> {
    let result1 = divide(a, b)?;
    let result2 = divide(result1, c)?;
    Ok(result2)
}
fn main() {
    match calculate(20.0, 2.0, 5.0) {
        Ok(result) => println!("Result: {}", result),
        Err(e) => println!("Error: {}", e),
    }
}

Explanation:

The ? operator automatically propagates the error if Result::Err, or unwraps the value if Result::Ok. This results in concise code.

Exercises

Result Function

Write a function that returns a Result!

Medium

Starter Code:

RUST
fn main() {
    match parse_number("42") {
        Ok(num) => println!("Number: {}", num),
        Err(e) => println!("Error: {}", e),
    }
}