Expand description
A library for ergonomic errors with call-site location data
This crate provides a StackError trait and stack_error proc macro to
ergonomically work with enum or struct errors.
-
All errors that use the macro will implement the
StackErrortrait, which exposes call-site metadata indicating where the error occurred. Itssourcemethod returns anErrorRef, which is an enum over either a reference to astd::error::Erroror anotherStackError. This allows retrieving error locations for the full error chain, as long as all errors are stack errors. -
The proc macro can add a
metafield to structs or enum variants. This field is the source for the call-site location accessed through theStackErrortrait. There is a simple declarative macroe!that provides an ergonomic way to construct errors with ametafield without having to spell it out everywhere. -
This crate also provides an
AnyErrortype, which is similar toanyhow. If constructed from an error that implementsStackError, the call-site location is preserved. TheAnyErrortype is generally recommended for applications or tests, whereas libraries should use concrete errors with the macro. -
There are result extensions to convert
StackErrors orstd::error::Errors toAnyErrorwhile providing additional context. -
While all errors using the derive macro from this crate have a
Fromimplementation forAnyError, regular std errors do not. Unfortunately, a blanketFromimplementation for all std errors would prevent a specialized implementation for stack errors, causing loss of location metadata upon conversion toAnyError. Therefore, you need to use thestd_contextoranyerrmethods to convert results with std errors toAnyError. You should not use these methods on stack errors; instead, usecontextor simply forward with?.
The call-site metadata in the meta field is collected only if the
environment variable RUST_BACKTRACE=1 or RUST_ERROR_LOCATION=1 is set.
Otherwise, it is not collected, as doing so has a small performance overhead.
Both AnyError and all errors that use the
derive macro feature the following outputs:
-
Display impl (
{error}) prints only the message of the outermost errorfailed to process input -
Alternate display impl (
{error:#}) prints the message for each error in the chain, in a single linefailed to process input: invalid input: wanted 23 but got 13 -
Debug impl (
{error:?}) prints the message and each source, on separate lines.failed to process input Caused by: invalid input wanted 23 but got 13If
RUST_BACKTRACEorRUST_ERROR_LOCATIONis set, this will also print the call site of each error.failed to process input (examples/basic.rs:61:17) Caused by: invalid input (examples/basic.rs:36:5) wanted 23 but got 13 (examples/basic.rs:48:13) -
Alternate debug impl
{error:#?}: An output similar to how the#[derive(Debug)]output looks.
§Feature flags
anyhow(off by default): EnablesFrom<anyhow::Error> for AnyError
§Example
use n0_error::{Result, StackResultExt, StdResultExt, e, stack_error};
/// The `stack_error` macro controls how to turn our enum into a `StackError`.
///
/// * `add_meta` adds a field to all variants to track the call-site error location
/// * `derive` adds `#[derive(StackError)]`
/// * `from_sources` creates `From` impls for the error sources
#[stack_error(derive, add_meta, from_sources)]
enum MyError {
/// We can define the error message with the `error` attribute
/// It should not include the error source, those are printed in addition depending on the output format.
#[error("invalid input")]
InvalidInput { source: InvalidInput },
/// Or we can define a variant as `transparent`, which forwards the Display impl to the error source
#[error(transparent)]
Io {
/// For sources that do not implement `StackError`, we have to mark the source as `std_err`.
#[error(std_err)]
source: std::io::Error,
},
}
/// We can use the [`stack_error`] macro on structs as well.
#[stack_error(derive, add_meta)]
#[error("wanted {expected} but got {actual}")]
struct InvalidInput {
expected: u32,
actual: u32,
}
fn validate_input(number: u32) -> Result<(), InvalidInput> {
if number != 23 {
// The `e` macro constructs a `StackError` while automatically adding the `meta` field.
Err(e!(InvalidInput {
actual: number,
expected: 23
}))
} else {
Ok(())
}
}
fn fail_io() -> std::io::Result<()> {
Err(std::io::Error::other("io failed"))
}
/// Some function that returns [`MyError`].
fn process(number: u32) -> Result<(), MyError> {
// We have a `From` impl for `InvalidInput` on our error.
validate_input(number)?;
// We have a `From` impl for `std::io::Error` on our error.
fail_io()?;
// Without the From impl, we'd need to forward the error manually.
// The `e` macro can assist here, so that we don't have to declare the `meta` field manually.
fail_io().map_err(|source| e!(MyError::Io, source))?;
Ok(())
}
// A main function that returns AnyError (via the crate's Result alias)
fn run(number: u32) -> Result<()> {
// We can add context to errors via the result extensions.
// The `context` function adds context to any `StackError`.
process(number).context("failed to process input")?;
// To add context to std errors, we have to use `std_context` from `StdResultExt`.
fail_io().std_context("failed at fail_io")?;
Ok(())
}
fn main() -> Result<()> {
if let Err(err) = run(13) {
println!("{err}");
// failed to process input
println!("{err:#}");
// failed to process input: invalid input: wanted 23 but got 13
println!("{err:?}");
// failed to process input
// Caused by:
// invalid input
// wanted 23 but got 13
// and with RUST_BACKTRACE=1 or RUST_ERROR_LOCATION=1
// failed to process input (examples/basic.rs:61:17)
// Caused by:
// invalid input (examples/basic.rs:36:5)
// wanted 23 but got 13 (examples/basic.rs:48:13)
println!("{err:#?}");
// Stack(WithSource {
// message: "failed to process input",
// source: Stack(InvalidInput {
// source: BadNumber {
// expected: 23,
// actual: 13,
// meta: Meta(examples/basic.rs:48:13),
// },
// meta: Meta(examples/basic.rs:36:5),
// }),
// meta: Meta(examples/basic.rs:61:17),
// })
}
Ok(())
}
/// You can also use the macros with tuple structs or enums.
/// In this case the meta field will be added as the last field.
#[stack_error(derive, add_meta)]
#[error("tuple fail ({_0})")]
struct TupleStruct(u32);
#[stack_error(derive, add_meta)]
#[allow(unused)]
enum TupleEnum {
#[error("io failed")]
Io(#[error(source, std_err)] std::io::Error),
}Macros§
- anyerr
- Converts a value into
AnyError. - bail
- Returns an error result by constructing a
StackErrorwithe. - bail_
any - Returns an error result by constructing an
AnyErrorwithanyerr. - e
- Constructs an error enum/struct value while automatically filling
meta: Meta. - ensure
- Ensures a condition, otherwise returns the error constructed with
efrom the remaining args. - ensure_
any - Ensures a condition, otherwise returns an
AnyError. - try_or
- Unwraps a result, returning in the error case while converting the error.
- try_
or_ any - Unwraps a result, returning in the error case while adding context to the error.
Structs§
- AnyError
- Type-erased error that can wrap a
StackErroror anystd::error::Error. - Chain
- Iterator over the sources of an error.
- Location
- Wrapper around
std::panic::Locationused for display in reports. - Meta
- Captured metadata for an error creation site.
- None
Error - Error returned when converting
Options to an error. - Report
- A
Reportcustomizes how an error is displayed.
Enums§
- Error
Ref - Reference to an error which can either be a std error or a stack error.
- Source
Format - Output style for rendering error sources in a
Report.
Traits§
- Stack
Error - Trait implemented by errors produced by this crate.
- Stack
Error Ext - Extension methods for
StackErrors that areSized. - Stack
Result Ext - Provides extension methods to add context to
StackErrors. - StdResult
Ext - Provides extension methods to add context to std errors.
Functions§
- Ok
- Returns a result with the error type set to
AnyError. - meta
- Creates new
Metacapturing the caller location.
Type Aliases§
Attribute Macros§
- stack_
error - Attribute macro to expand error enums or structs.
Derive Macros§
- Stack
Error - Derive macro that implements
StackError,Display,Debugandstd::error::Errorand generatesFrom<T>impls for fields/variants configured via#[error(..)]. Derive macro for stack errors.