Scoped Error: Yet another error handling crate, but this time it makes sense to me

59 points kanru 30 comments

This project is inspired by using the great exn crate in my projects.

Example:

use scoped_error::{Error, expect_error};

fn read_config() -> Result<String, Error> {
  expect_error("read config file", || {
    let raw = std::fs::read_to_string("config.toml")?;
    Ok(raw)
  })
}

fn complex_method() -> Result<(), Error> {
  expect_error("failed to do complex thing", || {
    let cfg = read_config()?;
    parse(cfg)?;
    Ok(())
  })
}

The core idea is simple: attach context exactly once. Not at every call site, not at every failure point, just between caller and logic. I want per-module Error types without manual conversion at every step.

expect_error() has three responsibilities: prepare context for future errors, force inner errors into a boxed type to type-erase the inner error, and wrap the outer error with the inner as its source.

The result: a clean, readable declaration of fallible operations. A default Error type is provided, but any std Error implementing WithContext works too.