# Errors – Result, Option ## `Option<T>` `Option<T>` represents a value that may or may not be present. It is commonly used when an operation might not yield a value but where this "absence" is an expected condition (e.g., retrieving a value from a `HashMap`). ```rust enum Option<T> { Some(T), None, } ``` ```rust fn safe_divide(a: f64, b: f64) -> Option<f64> { if b == 0.0 { None } else { Some(a / b) } } fn main() { match safe_divide(10.0, 2.0) { Some(result) => println!("Result: {}", result), None => println!("Cannot divide by zero"), } } ``` ## `Result<T, E>` `Result<T, E>` is used for operations that may succeed or fail, returning either a value (`Ok`) or an error (`Err`). It’s ideal for functions that may fail in various ways, allowing the caller to understand the specific reason behind a failure. ```rust enum Result<T, E> { Ok(T), Err(E), } ``` > [!info] `?` propagates errors upwards, returning `Err(e)` if a function fails: ```rust use std::fs::File; use std::io::{self, Read}; fn read_file(path: &str) -> Result<String, io::Error> { let mut file = File::open(path)?; let mut content = String::new(); file.read_to_string(&mut content)?; Ok(content) } ``` ## `thiserror` – Defining Custom Errors `thiserror` is a lightweight crate for defining structured custom error types. - Generates `Display` and `Debug` implementations. - Supports attaching error messages and causes. - Prefer `thiserror` for libraries because it's structured and explicit. ```rust use std::io; use thiserror::Error; /// Errors that can occur in the library. #[derive(Debug, Error)] pub enum MyLibError { #[error("Invalid input: {0}")] InvalidInput(String), #[error("IO error: {source}")] Io { #[from] source: io::Error, }, #[error("Parsing error: {source}")] Parse { #[from] source: std::num::ParseIntError, }, } pub fn parse_number_with_thiserror_exposed(input: &str) -> Result<i32, MyLibError> { let trimmed = input.trim(); if trimmed.is_empty() { return Err(MyLibError::InvalidInput("Empty string".to_string())); } let number: i32 = trimmed.parse()?; // Automatically converts ParseIntError via `#[from]` Ok(number) } fn main() -> Result<(), MyLibError> { parse_number_with_thiserror_exposed("123ABC")?; Ok(()) } ``` > [!under-contruction] #todo/wiki Add backtrace/stack_trace. ## `anyhow` – Simpler Error Handling `anyhow` provides a flexible `Error` type that can store different kinds of errors. - No need to define custom error types. - Easily wraps and propagates errors (`context()` helps add meaningful messages). - Works well for applications where precise error typing isn’t necessary. - For apps, `anyhow` simplifies error handling. ```rust use anyhow::{Context, Result}; use std::fs; fn read_config() -> Result<String> { let content = fs::read_to_string("config.toml") .context("Failed to read configuration file")?; Ok(content) } fn main() { match read_config() { Ok(config) => println!("Config: {}", config), Err(e) => println!("Error: {:?}", e), } } ``` ## References - https://www.youtube.com/watch?v=s5S2Ed5T-dc