error-rail
Composable, lazy-evaluated error handling for Rust.
std::error defines error types. error-rail defines how errors flow.
use *;
Features
- Lazy formatting — Use
context!/.ctx_with(...)to format strings only when errors occur - Chainable context — Stack multiple contexts with
ErrorPipeline::with_context() - Validation accumulation — Collect all errors, not just the first
- Transient error classification — Built-in retry support
- Error fingerprinting — Deduplicate errors in monitoring systems
- Async-first — Full async/await support with Tower & Tracing integration
no_stdcompatible — Works in embedded and web environments
Quick Start
For Beginners — simple
use *;
For General Use — prelude
use *;
API Levels
Most projects only need simple or prelude.
| Module | When to Use | What's Included |
|---|---|---|
simple |
Getting started | BoxedResult, rail!, .ctx(), .error_chain() |
prelude |
When structured context is needed | + context!, group!, ErrorPipeline |
intermediate |
Service development | + TransientError, Fingerprint |
advanced |
Library development | + internal builders, ErrorVec |
prelude_async |
Async code | + AsyncErrorPipeline, retry, timeout |
Context Chaining (Core Concepts)
1. ErrorPipeline — Recommended for multiple contexts
use *;
// Error chain: fetching user 42 -> [db] (user_id=42) -> querying users table -> db error
2. ResultExt — Single context
use *;
// .ctx() returns BoxedResult<T, E>
// Chain with .ctx_boxed() on BoxedResult
// Error chain: in user service -> querying database -> db error
3. Async — Direct chaining
use *;
async
Lazy Context (Performance Optimization)
use *;
let user_id = 42;
// ✅ context! — lazy evaluation (only on error)
result.ctx
// ✅ .ctx_with() — closure for complex logic
result.ctx_with
// ❌ format!() — always evaluated (even on success)
result.ctx
Structured Context
use *;
// Tags & metadata
result.ctx
Validation (Error Collection)
Use the
error_rail::validationmodule
use Validation;
let results: = vec!.into_iter.collect;
// All errors are collected (not just the first one)
Transient Errors & Retry
use TransientError;
use Duration;
Error Fingerprinting
use *;
let err = new
.with_context
.set_code;
// For Sentry grouping, log deduplication
println!;
Anti-Patterns
use *;
// ❌ DON'T: Excessive context at every step
// ✅ DO: One .ctx() per I/O boundary
When NOT to Use error-rail
- Simple scripts that just print errors and exit
- When
anyhoworeyrealready meets your needs - Teams with little Rust experience
Feature Flags
[]
= "0.10" # Core (no_std)
= { = "0.10", = ["std"] } # + backtraces
= { = "0.10", = ["serde"] } # + serde support
= { = "0.10", = ["async"] } # + async support
= { = "0.10", = ["tokio"] } # + retry, timeout
= { = "0.10", = ["tower"] } # + Tower middleware
= { = "0.10", = ["full"] } # Everything
Documentation
| Resource | Description |
|---|---|
| Quick Start | Step-by-step tutorial |
| Async Guide | Async patterns |
| Patterns | Real-world examples |
| Benchmarks | Performance analysis |
| API Docs | Full API reference |
Examples
Contributing
Issues and PRs are welcome!
Development
# Run tests
# Validate README examples
# Lint check
# Doc tests
Guidelines
- Bug reports: Submit to GitHub Issues with a reproducible example
- Feature requests: Discuss via issues first, then PR
- Pull Requests: Tests required, must pass
cargo clippy