2.12 Error Handling

Rust avoids exceptions, preferring Result<T, E> for recoverable errors and Option<T> for optional values. Both types are variants of Rust's powerful enum data type.

2.12.1 Result Type

fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err(String::from("Cannot divide by zero"))
    } else {
        Ok(a / b)
    }
}

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

The Result type provides the variants Ok and Err to indicate success or failure.

2.12.2 Option Type

fn divide(numerator: f64, denominator: f64) -> Option<f64> {
    if denominator == 0.0 {
        None
    } else {
        Some(numerator / denominator)
    }
}

fn main() {
    let result = divide(10.0, 2.0);
    match result {
        Some(value) => println!("Result: {}", value),
        None => println!("Cannot divide by zero"),
    }
}

Option has the variants Some(T) and None to indicate the presence or absence of a value.

2.12.3 Comparison with C

C functions often signal errors by returning a special code (like -1) and setting errno:

#include <stdio.h>
#include <errno.h>

int divide(double a, double b, double *result) {
    if (b == 0.0) {
        errno = EDOM;
        return -1;
    } else {
        *result = a / b;
        return 0;
    }
}

Rust's explicit types (Result, Option) reduce the ambiguity of error codes.