title: Result

Result<T, E> is Rust’s way of handling operations that can fail. Instead of throwing exceptions, functions return a Result that is either a success value or an error — and the compiler forces you to deal with both.

What it looks like

Result is an enum with two variants:

enum Result<T, E> {
    Ok(T),   // success, contains the value
    Err(E),  // failure, contains the error
}

A function that returns Result

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

Handling a Result with match

match divide(10.0, 2.0) {
    Ok(result) => println!("result: {result}"),
    Err(e) => println!("error: {e}"),
}

if let — a shorter alternative

When you only care about the success case:

if let Ok(result) = divide(10.0, 2.0) {
    println!("result: {result}");
}

Common methods

let result: Result<i32, String> = Ok(42);
 
result.is_ok();           // true
result.is_err();          // false
result.unwrap();          // returns 42, panics if Err — avoid in production
result.unwrap_or(0);      // returns 42, falls back to 0 if Err
result.unwrap_or_else(|e| { log(e); 0 });  // handle error and provide fallback
 
// transform the Ok value without unwrapping
result.map(|n| n * 2);   // Ok(84)
 
// transform the Err value
result.map_err(|e| format!("wrapped: {e}"));

The ? operator

The most common way to handle Result in real code. In a function that returns Result, ? will return the error early if the operation failed, otherwise unwrap the success value:

use std::fs;
 
fn read_file(path: &str) -> Result<String, std::io::Error> {
    let contents = fs::read_to_string(path)?;  // returns Err early if file can't be read
    Ok(contents)
}

Without ? you’d need to match every single operation — ? makes chained fallible operations readable:

fn process() -> Result<String, std::io::Error> {
    let config = fs::read_to_string("config.txt")?;
    let data = fs::read_to_string("data.txt")?;
    Ok(format!("{config}{data}"))
}

Result vs Option

Option<T>Result<T, E>
Use whena value may or may not existan operation may succeed or fail
Missing/failure caseNoneErr(E)
Success caseSome(T)Ok(T)
Carries error infonoyes

If you just need “something or nothing”, use Option. If you need to know why something failed, use Result.