JSTAcademy
0 XP
Dashboard
Crash Courses
Error Handling with Result & Option
35 min
Masters+180 XP
Crash Courses · Masters

Error Handling with Result & Option

Propagate errors without exceptions using the ? operator
35 min read+180 XP on completionCert: Rust Crash Course
Tap any word in the text below to start reading from there.

Error Handling with Result & Option

Result<T, E>

use std::num::ParseIntError;

fn parse_age(s: &str) -> Result<u32, ParseIntError> {
    let n = s.trim().parse::<u32>()?; // ? propagates the error
    Ok(n)
}

fn main() {
    match parse_age("25") {
        Ok(age)  => println!("Age: {}", age),
        Err(e)   => println!("Parse error: {}", e),
    }

    match parse_age("abc") {
        Ok(age)  => println!("Age: {}", age),
        Err(e)   => println!("Parse error: {}", e),
    }
}

The ? Operator

? is shorthand for:

// This:
let n = s.parse::<u32>()?;

// Expands to roughly:
let n = match s.parse::<u32>() {
    Ok(v)  => v,
    Err(e) => return Err(e.into()),
};

Chaining Operations

use std::fs;

fn read_username() -> Result<String, std::io::Error> {
    let content = fs::read_to_string("username.txt")?; // ? here
    Ok(content.trim().to_string())
}

Option Combinators

let s = "hello world";
let upper: Option<String> = s.split_whitespace()
    .next()            // Option<&str>
    .map(str::to_uppercase); // Option<String>

println!("{:?}", upper); // Some("HELLO")

// unwrap_or provides a default
let word = s.split_whitespace().next().unwrap_or("default");

Custom Error Types

use std::fmt;

#[derive(Debug)]
enum AppError {
    ParseError(String),
    NotFound(String),
}

impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AppError::ParseError(msg) => write!(f, "Parse error: {}", msg),
            AppError::NotFound(key)   => write!(f, "Not found: {}", key),
        }
    }
}

impl std::error::Error for AppError {}

fn find_user(id: u32) -> Result<String, AppError> {
    if id == 1 { Ok("Jordan".into()) }
    else { Err(AppError::NotFound(format!("id={}", id))) }
}

The anyhow Crate

For application code (not libraries), anyhow eliminates boilerplate:

// Cargo.toml: anyhow = "1"
use anyhow::{Result, Context};

fn run() -> Result<()> {
    let n: u32 = "42".parse().context("failed to parse config")?;
    println!("{}", n);
    Ok(())
}
0%