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 — Build rich error traces with
.ctx() - 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 — Start with simple:
use *;
For general use — Use prelude:
use *;
API Levels (You do NOT need to learn all of these)
Start here → simple
When you need more → prelude
Only if you are building services → intermediate
Almost never → advanced
| Module | When to Use | What's Included |
|---|---|---|
simple |
First time using error-rail | BoxedResult, rail!, .ctx(), .error_chain() |
prelude |
When you need structured context | + context!, group!, ErrorPipeline |
intermediate |
Building services | + TransientError, Fingerprint, formatting |
advanced |
Writing libraries | + internal builders, ErrorVec |
prelude_async |
Async code | + AsyncErrorPipeline, retry, timeout |
Core Concepts (Advanced — skip on first read)
If you are using
simple, you can skip this entire section.
Context Methods
// Static context (zero allocation)
result.ctx
// Lazy formatted context (evaluated only on error)
result.ctx
// NOTE: `result.ctx(format!(...))` is eager because `format!` runs before `.ctx()`.
// Structured context with tags & metadata
result.ctx
Validation (Collect All Errors)
Note: This is available in
error_rail::validation, not insimple.
use Validation;
let results: = vec!.into_iter.collect;
// Both errors collected, not just the first
Macros
use *;
// rail! - Wrap any Result in ErrorPipeline and box it
let result = rail!;
// context! - Lazy formatted context (only evaluated on error)
result.ctx
// group! - Structured context with tags & metadata
result.ctx
use *;
// rail_async! - Async version of rail!
async
Async Support
use *;
async
Anti-Patterns
// ❌ DON'T: Chain .ctx() multiple times
// ✅ DO: One .ctx() per I/O boundary
Avoid Glob Imports in Large Projects
For better IDE support and compile times in large codebases:
// ❌ Glob import (okay for small projects)
use *;
// ✅ Explicit imports (recommended for large projects)
use ;
When should I move from simple to prelude?
Move to prelude when you need:
- Structured context - tags and metadata for better error categorization
- Lazy formatted messages - use
context!/.ctx_with(...)so formatting only happens on error - ErrorPipeline - for building libraries or complex error chains
- Writing a library - not just an application
You can stay with
simplefor a long time! It's designed to be sufficient for most applications.
When NOT to Use error-rail
- Simple scripts that just print errors and exit
- Teams with little Rust experience
- When
anyhoworeyrealready meets your needs
Feature Flags
[]
= "0.9" # Core (no_std)
= { = "0.9", = ["std"] } # + backtraces
= { = "0.9", = ["serde"] } # + serde support
= { = "0.9", = ["async"] } # + async support
= { = "0.9", = ["tokio"] } # + retry, timeout
= { = "0.9", = ["tower"] } # + Tower middleware
= { = "0.9", = ["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