# [errs][repo-url] [![crates.io][cratesio-img]][cratesio-url] [![doc.rs][docrs-img]][docrs-url] [![CI Status][ci-img]][ci-url] [![MIT License][mit-img]][mit-url]
A library for handling errors with reasons for Rust
## Overview
`errs` is an error handling library for Rust designed to focus on the "Reason" behind an error.
### Expressing "Why It Failed" via the Type System
Rather than treating errors as simple message strings or type-erased objects, it embraces a design that expresses "why it failed" through types, allowing for safe and clear propagation and determination.
For error reasons, you can use anything from lightweight types like `String` to type-safe definitions using `enum` or `struct`, all handled flexibly with the same API.
By using an `enum` in particular, you can not only express failure factors within the type system but also hold contextual information in its fields, propagating the situation and relevant data at the time of the error as-is.
Furthermore, since reasons can be determined in a type-safe manner using `reason::<T>()` or `match_reason`, you can avoid fragile error handling that relies on string comparisons.
### Decentralized Error Definition and Traceability
`errs` encourages defining error reasons close to where they occur.
This eliminates the need to share a massive, monolithic error type across the entire application, enabling a highly maintainable design while keeping dependencies between modules clean.
Type information is utilized to identify the reason, and the type identifiers required for this determination are resolved statically at compile time.
This provides type-safe error handling with minimal runtime overhead.
The core `Err` type of the library implements `std::error::Error`, allowing it to integrate naturally with standard Rust error handling, including the `?` operator.
It can also retain lower-layer errors as causes (source errors), enabling you to manage the "Reason" of the upper layer and the "Cause" of the lower layer separately.
Additionally, it automatically records the file name and line number when an error is generated, making log output and failure analysis effortless.
### Powerful Error-Instantiation Notification & Monitoring Ecosystem
Furthermore, `errs` features a mechanism to notify error generation events.
By enabling the `notify` or `notify-tokio` feature, an automatic notification can be sent to registered handlers the exact moment an `Err` is created.
It supports synchronous handlers, generic asynchronous handlers, and asynchronous handlers tailored for Tokio.
It accommodates both dynamic registration within functions and static global registration via macros.
This makes it easy to implement logging, monitoring, metrics collection, and integration with telemetry systems.
While `anyhow`-like libraries place importance on "propagating errors flexibly," and `thiserror`-like libraries focus on "making error type definitions easy", `errs` emphasizes "expressing failure reasons through types and observing their occurrence".
This library is ideal when you want to clearly manage the semantics of errors occurring within an application while integrating seamlessly with monitoring and operations infrastructure.
## Install
In `Cargo.toml`, write this crate as a dependency:
```toml
[dependencies]
errs = "1.0.0"
```
If you want to use error notification, specify the `notify` or `notify-tokio` in the dependency features.
The `notify` feature is for general use, while the `notify-tokio` feature is for use with the Tokio runtime.
```toml
[dependencies]
errs = { version = "1.0.0", features = ["notify"] }
```
If you are using Tokio, you should specify `notify-tokio`:
```toml
[dependencies]
errs = { version = "1.0.0", features = ["notify-tokio"] }
```
## Usage
### Locally Defined Reasons and Instantiate an Err with Them
An `Err` struct can be instantiated with any arbitrary error reason.
Typically, a variant of an enum defined to indicate the cause or context of the error is used as the reason.
This variant does not need to belong to a single, centrally managed enum; rather, it is preferable to define it close to where the error using it as a reason actually occurs.
```rust
use errs::Err;
#[derive(Debug)]
enum Reasons {
IllegalState { state: String },
// ...
}
let err = Err::new(Reasons::IllegalState { state: "bad state".to_string() });
```
An `Err` can also be instantiated using `Err::with_source`, which accepts the underlying cause error along with the reason.
```rust
use std::io::{Error, ErrorKind};
use errs::Err;
let io_error = Error::new(ErrorKind::Interrupted, "oh no!");
let err = Err::with_source(Reasons::IllegalState { state: "bad state".to_string() }, io_error);
```
### Type-Safe Reason Identification
By using the `reason::<R>(&self)` method, you can extract the error reason as the specified type `R`.
Since the return value is `Result<&R, &Err>`, you can use a `match` statement to safely branch and identify the reason in a type-safe manner.
```rust
match err.reason::<Reasons>() {
Ok(r) => match r {
Reasons::IllegalState { state } => println!("state = {state}"),
_ => { /* ... */ }
}
Err(err) => match err.reason::<String>() {
Ok(s) => println!("string reason = {s}"),
Err(err) => { /* ... */ }
}
}
```
Methods like `match_reason` allow you to write error reason identification and fallback processing elegantly using method chaining, without having to nest multiple `match` statements.
```rust
let val: u8 = err.match_reason::<Reasons, u8>(|r| match r {
Reasons::IllegalState { _state } => Ok(1u8),
_ => Ok(2u8),
> To enable this feature, you must specify the feature `notify` or `notify-tokio` in `Cargo.toml`.
Alternatively, you can register handlers from a static context (outside a function body)
using macros. These are useful for setting up global handlers that are compiled into your
program.
- `add_sync_err_handler!`: Statically registers a synchronous handler.
- `add_async_err_handler!`: Statically registers a general-purpose asynchronous handler.
- `add_tokio_async_err_handler!`: Statically registers a Tokio-based asynchronous handler.
These macros require function pointers, not closures.
```rust
#[cfg(feature = "notify")]
use errs::{add_async_err_handler, add_sync_err_handler};
#[cfg(feature = "notify-tokio")]
use errs::{add_tokio_async_err_handler};
use errs::Err;
use chrono::{DateTime, Utc};
use std::sync::Arc;
// Define a static synchronous handler
fn my_sync_handler(err: &Err, tm: DateTime<Utc>) {
println!("[Static Sync] Error at {}: {}", tm, err);
}
#[cfg(feature = "notify")]
add_sync_err_handler!(my_sync_handler);
// Define a static asynchronous handler
fn my_async_handler(err: &Err, tm: DateTime<Utc>) {
println!("[Static Async] Error at {}: {}", tm, err);
}
#[cfg(feature = "notify")]
add_async_err_handler!(my_async_handler);
// Define a static Tokio-based asynchronous handler
#[cfg(feature = "notify-tokio")]
});
// Later, in your main function, you still need to fix the handlers.
// errs::fix_err_handlers();
```
## Supported Rust versions
This crate supports Rust 1.80.1 or later.
```bash
% ./build.sh msrv
[Meta] cargo-msrv 0.19.3
Compatibility Check #1: Rust 1.77.2
[FAIL] Is incompatible
Compatibility Check #2: Rust 1.87.0
[OK] Is compatible
Compatibility Check #3: Rust 1.82.0
[OK] Is compatible
Compatibility Check #4: Rust 1.79.0
[FAIL] Is incompatible
Compatibility Check #5: Rust 1.80.1
[OK] Is compatible
Result:
Considered (min … max): Rust 1.56.1 … Rust 1.97.1
Search method: bisect
MSRV: 1.80.1
Target: x86_64-apple-darwin
```
## License
Copyright (C) 2025-2026 Takayuki Sato
This program is free software under MIT License.<br>
See the file LICENSE in this distribution for more details.
[repo-url]: https://github.com/sttk/errs-rust
[cratesio-img]: https://img.shields.io/badge/crates.io-ver.1.0.0-fc8d62?logo=rust
[cratesio-url]: https://crates.io/crates/errs
[docrs-img]: https://img.shields.io/badge/doc.rs-errs-66c2a5?logo=docs.rs
[docrs-url]: https://docs.rs/errs
[ci-img]: https://github.com/sttk/errs-rust/actions/workflows/rust.yml/badge.svg?branch=main
[ci-url]: https://github.com/sttk/errs-rust/actions?query=branch%3Amain
[mit-img]: https://img.shields.io/badge/license-MIT-green.svg
[mit-url]: https://opensource.org/licenses/MIT