okerr is a ergonomic result / error handling helpers built on anyhow and thiserror.
Most Rust projects handle errors with anyhow or eyre, and thiserror (for derive(Error)). Sometimes we have to juggle between.
okerr solves the problem, this crate makes this more convenient by providing consistency and an idiomatic API.
The okerr crate is mostly re-exported from anyhow, making it 100% compatible in both directions.
Thanks to okerr::from_boxed_error, it's easy to capture and convert eyre errors without including eyre as a dependency!
All of that in just a few lines of code (very lightweight, no overhead, no abstraction cost). Fully tested, of course!
Idiomatic aliases for clearer code:
anyerr!("Oops!"); // Instead of `anyhow!("Oops!")
err!("Oops!"); // Instead of `Err(anyhow!("Oops!")) or `Err(anyerr!("Oops!"))
fail!("Oops!"); // Instead of `bail!("Oops!")
// ---
use okerr::derive::Error;
#[derive(Error, Debug)]
// Instead of
use thiserror::Error;
#[derive(Error, Debug)]
// ---
// Convert from a boxed error (like eyre::Report)
let error = okerr::from_boxed_error(error.into())
// Create/convert from any std::error::Error
let error = okerr::anyerr!(error)
// Create/convert from a string
let error = okerr::anyerr!("Oops!")
// Wrap a Result into an okerr::Error
// Equivalent to result.map_err(okerr::Error::new)
let result = okerr::wrap_err(result)It's very simple, very lightweight (just a few lines of code in the okerr crate), it provides consistency and a excellent DX. 100% compatible with anyhow and thiserror, convert easily error from a boxed error (like eyre::Report and others).
With okerr::Result, okerr::err!, okerr::fail! and okerr::anyerr!:
use okerr::{Result, err, fail, anyerr};
fn divide(a: i32, b: i32) -> Result<i32> {
if b == 0 {
err!("Cannot divide by zero")
} else {
Ok(a / b)
}
}
fn maybe_fail(should_fail: bool) -> Result<String> {
if should_fail {
fail!("Oops!");
}
Ok("No error".to_string())
}
fn main() {
let result = divide(10, 2);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 5);
let result = divide(10, 0);
assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), "Cannot divide by zero");
// fail! does early return
let result = maybe_fail(true);
assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), "Oops!");
// No error
let result = maybe_fail(false);
assert!(result.is_ok());
assert_eq!(result.unwrap(), "No error");
// Same as anyhow!(...).
// Creates an Error directly,
// from a string or any std::error::Error
let error = anyerr!("Oops!");
assert_eq!(error.to_string(), "Oops!");
}NOTE: requieres the
thiserrordependency to be added to yourCargo.toml(cargo add thiserror).
With okerr::derive::Error:
use okerr::{Result, err, derive::Error};
#[derive(Error, Debug)]
enum MyError {
#[error("Cannot divide by zero")]
DivideByZero,
#[error("Cannot divide by {0}")]
DivideBy(i32),
}
fn divide(a: i32, b: i32) -> Result<i32> {
if b == 0 {
err!(MyError::DivideByZero)
} else if b < 0 {
err!(MyError::DivideBy(b))
} else {
Ok(a / b)
}
}
fn main() {
let result = divide(10, 2);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 5);
let result = divide(10, 0);
assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), "Cannot divide by zero");
let result = divide(10, -2);
assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), "Cannot divide by -2");
}okerr is fully tested! Run all tests:
cargo testThanks to @dtolnay, the author of anyhow and thiserror, for providing two great ways to handle errors in Rust. 👍
MIT (c) 2025, Nicolas Talle.
Buy me a coffee ☕ via PayPal!