neuer-error 0.2.0

Ergonomic error handling for machines and humans.
Documentation

Neuer Error

crates.io page docs.rs page license: MIT

The error that can be whatever you want (it is Mr. Neuer). In every case (hopefully). NO AI SLOP!

An error handling library designed to be:

  • Useful in both libraries and applications, containing human and machine information.
  • Ergonomic, low-boilerplate and comfortable, while still adhering best-practices and providing all necessary infos.
  • Flexible in interfacing with other error handling libraries.

Features

  • Most importantly: error messages, that are helpful for debugging. By default it uses source locations instead of backtraces, which is often easier to follow, more efficient and works without debug info.
  • Discoverable, typed context getters without generic soup, type conversions and conflicts.
  • Works with std and no-std, but requires a global allocator. See example.
  • Compatible with non-Send/Sync environments, but also with Send/Sync environments (per feature flag).
  • Out of the box source error chaining.
  • No dependencies by default. Optional features may lead to some dependencies.
  • No unsafe used (yet?).

Why a new (German: neuer) error library?

Long story, you can view it here.

TLDR: I wasn't satisfied with my previous approach and existing libraries I know. And I was inspired by a blog post to experiment myself with error handling design.

Usage

The best way to see how to use it for your use-case is to check out the examples. Nevertheless, here is a quick demo:

// In library/module:
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum Retryable { No, Yes }

// Provide discoverable, typed information for library users.
provided_attachments!(
  retryable(single: Retryable) -> bool {
    |retryable| matches!(retryable, Some(Retryable::Yes))
  };
);

fn do_something_internal() -> Result<()> {
  Err(NeuErr::new("Error occurred internally")
    .attach(Retryable::No))
}

pub fn do_something() -> Result<()> {
  do_something_internal().context("Operation failed")
}

// In consumer/application:
fn main() {
  match do_something() {
    Ok(()) => {}
    Err(err) if err.retryable() => {
      eprintln!("Retryable error");
    }
    Err(_) => {
      eprintln!("Non-retryable error");
    }
  }
}

Run cargo add neuer-error to add the library to your project.

Comparisons

Anyhow / Eyre

  • NeuErr provides a meechanism to discover and retrieve multiple items of typed context information, while anyhow can downcast to its source error types only.
  • NeuErr captures source locations instead of backtraces by default, which is more efficient and works without debug info. I personally also find it easier to read.

Thiserror / Snafu

  • NeuErr is a single error type for all errors, so no need for boilerplate, better ergonomics, but less type safety and flexibility.
  • NeuErr captures source location automatically, which thiserror does not and snafu does only when you add the location field to every error variant.
  • NeuErr prints the full (source) error chain already.
  • NeuErr does not have procedural macros.

Development

If you want to contribute or have questions, feel free to open issues :) Always better to ask before investing too much effort into PRs that I won't accept.

Running all the checks is quite simple:

  1. Install cargo-make: cargo install cargo-make.
  2. Optional, but recommended: Put search_project_root = true into cargo-make's user configuration, so that cargo make can be run from sub-folders.
  3. From the project directory, you can run the following tasks:
    • Run all checks that are done in CI: cargo make ci or just cargo make.
    • Format code: cargo make format.
    • Check formatting: cargo make formatting.
    • Run all tests via cargo test: cargo make test.
    • Run clippy for all feature sets, failing on any warnings: cargo make clippy.

Minimum supported Rust version (MSRV)

Currently, I am always using the latest Rust version and do not put in any effort to keep the MSRV. Please open an issue in case you need a different policy, I might consider changing the policy.

License

Licensed under the MIT license. All contributors must agree to publish under this license.