simplified rust error handling
Find a file
Harald Hoyer 101d2074e1
feat: removed prelude
It was causing the rust compiler to output the renamed structs in error
messages confusing users who don't know about the old renamed ones.

Signed-off-by: Harald Hoyer <harald@hoyer.xyz>
2023-07-28 17:04:39 +02:00
.github ci: use ubuntu-latest 2023-07-28 15:13:50 +02:00
booksrc docs: use new names 2023-07-28 16:22:00 +02:00
examples feat: add new(Into<String>) method for str_context! types 2023-07-28 16:32:46 +02:00
src feat: removed prelude 2023-07-28 17:04:39 +02:00
tests tests: use new names 2023-07-28 16:20:55 +02:00
.gitignore add git-deploy-branch.sh 2018-12-21 16:10:05 +01:00
book.toml lots of documentation 2018-12-21 13:50:08 +01:00
Cargo.toml chore: Release chainerror version 0.8.2 2023-07-28 15:18:14 +02:00
git-deploy-branch.sh add git-deploy-branch.sh 2018-12-21 16:10:05 +01:00
LICENSE-APACHE add LICENSE-APACHE 2018-12-21 13:57:18 +01:00
LICENSE-MIT readd LICENSE-MIT 2018-12-21 13:59:11 +01:00
README.md docs: use new names 2023-07-28 16:22:00 +02:00

Crate Rust Documentation Coverage Status Maintenance

chainerror

chainerror provides an error backtrace without doing a real backtrace, so even after you strip your binaries, you still have the error backtrace.

Having nested function returning errors, the output doesn't tell where the error originates from.

use std::path::PathBuf;

type BoxedError = Box<dyn std::error::Error + Send + Sync>;
fn read_config_file(path: PathBuf) -> Result<(), BoxedError> {
    // do stuff, return other errors
    let _buf = std::fs::read_to_string(&path)?;
    // do stuff, return other errors
    Ok(())
}

fn process_config_file() -> Result<(), BoxedError> {
    // do stuff, return other errors
    let _buf = read_config_file("foo.txt".into())?;
    // do stuff, return other errors
    Ok(())
}

fn main() {
    if let Err(e) = process_config_file() {
        eprintln!("Error:\n{:?}", e);
    }
}

This gives the output:

Error:
Os { code: 2, kind: NotFound, message: "No such file or directory" }

and you have no idea where it comes from.

With chainerror, you can supply a context and get a nice error backtrace:

use chainerror::Context as _;
use std::path::PathBuf;

type BoxedError = Box<dyn std::error::Error + Send + Sync>;
fn read_config_file(path: PathBuf) -> Result<(), BoxedError> {
    // do stuff, return other errors
    let _buf = std::fs::read_to_string(&path).context(format!("Reading file: {:?}", &path))?;
    // do stuff, return other errors
    Ok(())
}

fn process_config_file() -> Result<(), BoxedError> {
    // do stuff, return other errors
    let _buf = read_config_file("foo.txt".into()).context("read the config file")?;
    // do stuff, return other errors
    Ok(())
}

fn main() {
    if let Err(e) = process_config_file() {
        eprintln!("Error:\n{:?}", e);
    }
}

with the output:

Error:
examples/simple.rs:14:51: read the config file
Caused by:
examples/simple.rs:7:47: Reading file: "foo.txt"
Caused by:
Os { code: 2, kind: NotFound, message: "No such file or directory" }

chainerror uses .source() of std::error::Error along with #[track_caller] and Location to provide a nice debug error backtrace. It encapsulates all types, which have Display + Debug and can store the error cause internally.

Along with the Error<T> struct, chainerror comes with some useful helper macros to save a lot of typing.

chainerror has no dependencies!

Debug information is worth it!

Tutorial

Read the Tutorial

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.