Eyre is a Rust library designed to provide a delightful and robust error handling experience, especially for application-level errors. It aims to make it easy to generate, propagate, and display rich, user-friendly error messages with optional backtraces.
Key features of `eyre`:
1. `eyre::Report`: The central error type. Unlike `std::error::Error`, `Report` is a "fat" error object capable of storing any type that implements `std::error::Error`. It automatically captures a backtrace when created, which is invaluable for debugging.
2. `eyre::Result<T>`: A type alias for `std::result::Result<T, eyre::Report>`. This simplifies function signatures, allowing `?` operator usage for propagating `eyre::Report` errors.
3. Context and `WrapErr`: `eyre` provides the `WrapErr` trait, which extends `Result` with methods like `.wrap_err("message")` and `.wrap_err_with(|| format!("message: {}", value))`. These methods allow you to add descriptive context to an error as it propagates up the call stack, transforming generic errors into specific, actionable diagnostics.
4. `eyre!` macro: A convenient macro for creating new `eyre::Report` errors from a formatted string, similar to `panic!`, but returning an error.
5. Displaying Errors: `Report` implements `std::fmt::Display` and `std::fmt::Debug`. When printed with `{:?}`, it provides a detailed output including the error chain (cause of causes) and, if enabled, a backtrace. This makes `eyre` errors very informative.
6. Comparison to other error handling libraries:
* `anyhow`: `eyre` is very similar to `anyhow` but offers a slightly more opinionated approach with a focus on user-facing diagnostics and often a more structured `Report` type, giving finer control over how errors are presented.
* `thiserror`: `thiserror` is used for defining concrete, specific error types (enums or structs) for libraries, allowing consumers to match against specific errors. `eyre` (like `anyhow`) is generally used at the application boundary or in `main` functions where the exact error type isn't as critical as providing good diagnostic information.
Example Code
use eyre::{eyre, Result, WrapErr};
use std::fs;
use std::path::Path;
// A function that attempts to read a number from a file, parse it,
// and return double its value, or an error if anything goes wrong.
fn get_magic_number(path: &Path) -> Result<i32> {
// Try to read the file content.
// If it fails, add context saying WHICH file failed to read.
let content = fs::read_to_string(path)
.wrap_err_with(|| format!("Failed to read file at \"{}\"", path.display()))?;
// Try to parse the string content into an i32.
// If it fails, add context saying what content couldn't be parsed.
let number: i32 = content.trim().parse()
.wrap_err_with(|| format!("Failed to parse number from file content: '{}'", content.trim()))?;
// Add a custom application-specific error if the number is zero.
if number == 0 {
return Err(eyre!("Cannot process zero as a magic number: input was 0."));
}
Ok(number * 2)
}
fn main() -> Result<()> {
// --- Scenario 1: Valid file and content ---
let path_valid = Path::new("test_input.txt");
fs::write(path_valid, "10").wrap_err("Failed to write initial test file")?;
println!("\n--- Scenario 1: Valid Input ---");
match get_magic_number(path_valid) {
Ok(result) => println!("Processed result: {}", result), // Expected: 20
Err(e) => eprintln!("Error: {:?}", e),
}
fs::remove_file(path_valid).ok(); // Clean up
// --- Scenario 2: File not found ---
let path_not_found = Path::new("non_existent.txt");
println!("\n--- Scenario 2: File Not Found ---");
match get_magic_number(path_not_found) {
Ok(result) => println!("Processed result: {}", result),
Err(e) => eprintln!("Error: {:?}", e), // Expecting "Failed to read file..." with source error
}
// --- Scenario 3: Invalid file content (not a number) ---
let path_invalid_content = Path::new("invalid_content.txt");
fs::write(path_invalid_content, "hello rust").wrap_err("Failed to write invalid content file")?;
println!("\n--- Scenario 3: Invalid File Content ---");
match get_magic_number(path_invalid_content) {
Ok(result) => println!("Processed result: {}", result),
Err(e) => eprintln!("Error: {:?}", e), // Expecting "Failed to parse number..." with source error
}
fs::remove_file(path_invalid_content).ok(); // Clean up
// --- Scenario 4: Custom application logic error (number is zero) ---
let path_zero_input = Path::new("zero_input.txt");
fs::write(path_zero_input, "0").wrap_err("Failed to write zero input file")?;
println!("\n--- Scenario 4: Custom Error (Zero Input) ---");
match get_magic_number(path_zero_input) {
Ok(result) => println!("Processed result: {}", result),
Err(e) => eprintln!("Error: {:?}", e), // Expecting "Cannot process zero as a magic number..."
}
fs::remove_file(path_zero_input).ok(); // Clean up
Ok(())
}








eyre