error-rail
use *;
Why error-rail?
Most error handling libraries format context eagerly—even on success paths where the context is never used. error-rail uses lazy evaluation, deferring string formatting until an error actually occurs.
Benchmark Results (full methodology)
| Scenario | error-rail | Eager format!() |
Speedup |
|---|---|---|---|
| Success path (95% of cases) | 637 ns | 1,351 ns | 2.1x faster |
| Error path (5% of cases) | 941 ns | 835 ns | 1.1x slower |
Benchmark Details
- Environment: Rust 1.81.0, criterion 0.7
- Comparison: Lazy
context!()vs eagerformat!()evaluation - Real-world impact: With typical 95% success rate, ~1.8x overall improvement
- Error path overhead: Extra ~100ns for lazy evaluation on errors
Since most operations succeed (95%+), lazy evaluation provides significant real-world performance gains.
Requirements
- Rust: 1.81.0 or later (MSRV)
- Edition: 2021
Installation
Or add to Cargo.toml:
[]
= "0.5"
30-Second Quick Start
use *;
// Add context to any Result with .ctx()
// Chain multiple contexts
New to error-rail? See the Quick Start Guide for step-by-step examples.
Key Features
1. Structured Error Context
Wrap any error in ComposableError and attach layered metadata.
use ;
let err = new
.with_context
.with_context
.set_code;
println!;
// Output: retry attempt 3 -> [database] at src/main.rs:7 (host=localhost:5432) -> connection failed (code: 500)
2. Error Pipeline
Chain context and transformations with a fluent builder API.
use ;
let result = new
.with_context
.with_context
.map_error // Transform error type
.finish_boxed;
if let Err = result
3. Validation Accumulation
Collect multiple errors instead of failing fast—ideal for form validation.
use Validation;
// Collect all validation results
let results: = vec!.into_iter.collect;
match results
// Output:
// Error: age must be between 0 and 150
// Error: name cannot be empty
4. Zero-Cost Lazy Context
The context! macro defers string formatting until an error actually occurs.
use ;
let payload = LargePayload ;
// format!() is NEVER called on success path
let result = new
.with_context
.finish_boxed;
Performance: In benchmarks, lazy context adds near-zero overhead on success. Eager formatting can be orders of magnitude slower.
5. Convenient Macros
| Macro | Purpose | Example |
|---|---|---|
context! |
Lazy formatted message | context!("user_id: {}", id) |
group! |
Structured context with multiple fields | group!(tag("db"), location(file!(), line!())) |
rail! |
Quick pipeline wrapper | rail!(fallible_fn()) |
use ;
// rail! is shorthand for ErrorPipeline::new(...).finish_boxed()
let result = rail!;
6. Type Aliases for Ergonomics
use *;
// Recommended: BoxedResult for public API (8 bytes stack)
// Alternative: ComposableResult for internal use (larger stack)
use ComposableResult;
Type Aliases Comparison
| Type Alias | Definition | Stack Size | Use When |
|---|---|---|---|
BoxedResult<T, E> |
Result<T, Box<ComposableError<E>>> |
8 bytes | Recommended for public APIs |
BoxedComposableResult<T, E> |
Same as BoxedResult |
8 bytes | Legacy alias (identical) |
ComposableResult<T, E> |
Result<T, ComposableError<E>> |
48+ bytes | Internal functions only |
Note:
BoxedResultandBoxedComposableResultare identical. UseBoxedResultfor brevity.
When to Use What?
Quick Reference
| Scenario | Recommended Type | Example |
|---|---|---|
| Simple error wrapping | ComposableError<E> |
Internal error handling |
| Function return type | BoxedResult<T, E> |
Public API boundaries |
| Adding context to Result | ErrorPipeline |
Wrapping I/O operations |
| Form/input validation | Validation<E, T> |
Collecting all field errors |
| Error chaining | ErrorPipeline + finish_boxed() |
Multi-step operations |
Validation vs Result
| Feature | Result<T, E> |
Validation<E, T> |
|---|---|---|
| Short-circuit | ✅ Yes (stops at first error) | ❌ No (collects all) |
| Use case | Sequential operations | Parallel validation |
| Error count | Single | Multiple |
| Iterator support | ? operator |
.collect() |
Common Pitfalls
1. Forgetting to Box for Return Types
// ❌ Large stack size (48+ bytes per Result)
// ✅ Reduced stack size (8 bytes pointer)
2. Excessive Context Depth
// ❌ Adding context at every layer (O(n) performance)
db_call
.with_context
.and_then
.and_then
// ... 20 more layers
// ✅ Add context at boundaries only
let result = db_call
.and_then
.and_then;
new
.with_context
.finish_boxed
3. Eager vs Lazy Context
// ❌ Eager: format! runs even on success
.with_context
// ✅ Lazy: format! only runs on error
.with_context
Module Reference
| Module | Description |
|---|---|
prelude |
Start here! Common imports: ResultExt, BoxedResult, macros |
context |
Context attachment: with_context, accumulate_context, format_error_chain |
convert |
Conversions between Result, Validation, and ComposableError |
macros |
context!, group!, rail!, impl_error_context! |
traits |
ResultExt, BoxedResultExt, IntoErrorContext, ErrorOps, WithError |
types |
ComposableError, ErrorContext, ErrorPipeline, LazyContext |
validation |
Validation<E, A> type with collectors and iterators |
Feature Flags
| Feature | Description | Default |
|---|---|---|
std |
Standard library support (enables backtrace! macro) |
❌ No |
serde |
Serialization/deserialization support | ❌ No |
full |
Enable all features (std + serde) |
❌ No |
Usage Examples
# Default (no_std compatible, requires alloc)
[]
= "0.5"
# With std library support (e.g., for backtraces)
[]
= { = "0.5", = ["std"] }
# With serialization support
[]
= { = "0.5", = ["serde"] }
# All features enabled
[]
= { = "0.5", = ["full"] }
no_std Support
error-rail is no_std compatible by default. It requires only the alloc crate.
# Minimal no_std usage
[]
= { = "0.5", = false }
Note: Some features like
backtrace!require thestdfeature.
Examples
Integration Guides
- Quick Start Guide - Step-by-step tutorial
Glossary
| Term | Definition |
|---|---|
| Context | Additional information attached to an error (location, tags, messages) |
| Metadata | Key-value pairs within context (subset of context) |
| Pipeline | Builder pattern for chaining error operations (ErrorPipeline) |
| Boxed Error | Heap-allocated error via Box<ComposableError<E>> for reduced stack size |
| Lazy Evaluation | Deferred computation until actually needed (e.g., context! macro) |
| Validation | Accumulating type that collects all errors instead of short-circuiting |
License
Licensed under the Apache License 2.0. See LICENSE for details.
For third-party attributions, see NOTICE.