# 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