#error #report #error-context #error-handling #debugging #derive-debug #no-std

no-std error-stack

A context-aware error-handling library that supports arbitrary attached user data

11 unstable releases (3 breaking)

0.4.1 Sep 4, 2023
0.4.0 Aug 23, 2023
0.3.1 Feb 8, 2023
0.2.4 Nov 4, 2022
0.1.1 Jun 10, 2022

#43 in Rust patterns

Download history 9098/week @ 2024-01-03 9718/week @ 2024-01-10 10739/week @ 2024-01-17 11729/week @ 2024-01-24 11703/week @ 2024-01-31 13015/week @ 2024-02-07 13826/week @ 2024-02-14 15628/week @ 2024-02-21 14714/week @ 2024-02-28 14364/week @ 2024-03-06 11744/week @ 2024-03-13 14776/week @ 2024-03-20 11164/week @ 2024-03-27 15918/week @ 2024-04-03 13107/week @ 2024-04-10 12720/week @ 2024-04-17

55,799 downloads per month
Used in 67 crates (61 directly)

MIT/Apache

215KB
3K SLoC

crates.io libs.rs rust-version documentation license discord

Open issues / Discussions

error-stack

error-stack is a context-aware error-handling library that supports arbitrary attached user data.

Read our announcement post for the story behind its origins.

The library enables building a Report around an error as it propagates:

use std::fmt;

use error_stack::{Context, Report, Result, ResultExt};

#[derive(Debug)]
struct ParseExperimentError;

impl fmt::Display for ParseExperimentError {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt.write_str("invalid experiment description")
    }
}

impl Context for ParseExperimentError {}

fn parse_experiment(description: &str) -> Result<(u64, u64), ParseExperimentError> {
    let value = description
        .parse::<u64>()
        .attach_printable_lazy(|| format!("{description:?} could not be parsed as experiment"))
        .change_context(ParseExperimentError)?;

    Ok((value, 2 * value))
}

#[derive(Debug)]
struct ExperimentError;

impl fmt::Display for ExperimentError {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt.write_str("experiment error: could not run experiment")
    }
}

impl Context for ExperimentError {}

fn start_experiments(
    experiment_ids: &[usize],
    experiment_descriptions: &[&str],
) -> Result<Vec<u64>, ExperimentError> {
    let experiments = experiment_ids
        .iter()
        .map(|exp_id| {
            let description = experiment_descriptions.get(*exp_id).ok_or_else(|| {
                Report::new(ExperimentError)
                    .attach_printable(format!("experiment {exp_id} has no valid description"))
            })?;

            let experiment = parse_experiment(description)
                .attach_printable(format!("experiment {exp_id} could not be parsed"))
                .change_context(ExperimentError)?;

            Ok(move || experiment.0 * experiment.1)
        })
        .collect::<Result<Vec<_>, ExperimentError>>()
        .attach_printable("unable to set up experiments")?;

    Ok(experiments.iter().map(|experiment| experiment()).collect())
}

fn main() -> Result<(), ExperimentError> {
    let experiment_ids = &[0, 2];
    let experiment_descriptions = &["10", "20", "3o"];
    start_experiments(experiment_ids, experiment_descriptions)?;

    Ok(())
}

This will most likely result in an error and print

Error: experiment error: could not run experiment
├╴at examples/demo.rs:50:18
├╴unable to set up experiments
│
├─▶ invalid experiment description
│   ├╴at examples/demo.rs:20:10
│   ╰╴experiment 2 could not be parsed
│
╰─▶ invalid digit found in string
    ├╴at examples/demo.rs:19:10
    ├╴backtrace with 31 frames (1)
    ╰╴"3o" could not be parsed as experiment

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

backtrace no. 1
   0: std::backtrace_rs::backtrace::libunwind::trace
             at /rustc/f3623871cfa0763c95ebd6ceafaa6dc2e44ca68f/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5
   1: std::backtrace_rs::backtrace::trace_unsynchronized
             at /rustc/f3623871cfa0763c95ebd6ceafaa6dc2e44ca68f/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
   2: std::backtrace::Backtrace::create
             at /rustc/f3623871cfa0763c95ebd6ceafaa6dc2e44ca68f/library/std/src/backtrace.rs:331:13
   3: core::ops::function::FnOnce::call_once
             at /rustc/f3623871cfa0763c95ebd6ceafaa6dc2e44ca68f/library/core/src/ops/function.rs:250:5
   4: core::bool::<impl bool>::then
             at /rustc/f3623871cfa0763c95ebd6ceafaa6dc2e44ca68f/library/core/src/bool.rs:60:24
   5: error_stack::report::Report<C>::from_frame
             at ./src/report.rs:286:25
   6: error_stack::report::Report<C>::new
             at ./src/report.rs:272:9
   7: error_stack::context::<impl core::convert::From<C< for error_stack::report::Report<C>>::from
             at ./src/context.rs:83:9
   8: <core::result::Result<T,C< as error_stack::result::ResultExt<::attach_printable_lazy
             at ./src/result.rs:158:31
   9: demo::parse_experiment
             at demo.rs:17:17
  10: demo::start_experiments::{{closure}}
             at demo.rs:48:30
   (For this example: additional frames have been removed)

Usage

Please see the documentation.

For more examples of error-stack in use, please check out the examples folder.

Contributors

error-stack was created and is maintained by HASH. As an open-source project, we gratefully accept external contributions and have published a contributing guide that outlines the process. If you have questions, please reach out to us on our Discord server.

License

error-stack is available under a number of different open-source licenses. Please see the LICENSE file to review your options.

Dependencies

~0–620KB
~11K SLoC