[−][src]Crate err_context
Lightweight error context manipulation.
Oftentimes, when error is being reported to the user, the lowest-level cause of the original error is not very interesting (eg. „No such file or directory“). It is not clear what file it should have been, why the program needed that file and what the consequences of failing to find it are.
This library allows to wrap the low-level error into layers of context easily and examine such multi-layer libraries. Depending on preferences of the developer, the story of the whole error might then be found either in the outer layer, or the whole chain may be needed.
There are other libraries with similar aim, though none seemed to fit very well. Most of them
have unergonomic API. This API is modeled after the failure
crate (which has simple and
powerful API), but uses the Error
trait in standard library.
Compatibility
By using the trait and AnyError
type alias, the library is compatible with any future or
past version of self or any other error handling library that operates on the Error
trait.
To avoid dependencies on specific version of this library, downstream crates are advised to not
reexport types from here with the sole exception of the AnyError
alias (as it is alias, it
can be re-created independently by as many libraries and is compatible). Downstream libraries
are, of course, free to expose their own errors.
Using the library
Besides the AnyError
alias, users of the library probably don't want to use any of the
present types here directly. Instead, certain traits can be imported and all errors, boxed
errors and results containing them get additional methods.
The methods can be found on the ErrorExt
and ResultExt
traits.
Producing errors
The lowest level error comes from somewhere else ‒ it may be type provided by some other crate,
an error implemented manually or generated with help of some other crate (the err-derive
crate offers derive macro similar to the one of failure
, but for standard errors ‒
combination of these two crates provide almost all the functionality of failure
).
Then, as the error bubbles up, it can be enriched by additional information using the
.context
and .with_context
methods.
use std::error::Error; use std::io::Read; use std::fmt::{Display, Formatter, Result as FmtResult}; use std::fs::File; use std::path::Path; use err_context::AnyError; use err_context::prelude::*; // An example error. You'd implement it manually like this, or use something else, like // err-derive, to generate it. #[derive(Debug)] struct BrokenImage; impl Display for BrokenImage { fn fmt(&self, fmt: &mut Formatter) -> FmtResult { write!(fmt, "The image is broken") } } impl Error for BrokenImage {} #[derive(Clone, Debug)] struct Image; impl Image { fn parse(_: Vec<u8>) -> Result<Self, BrokenImage> { // This is an example. We didn't really implement this. Err(BrokenImage) } } fn read_image<P: AsRef<Path>>(path: P) -> Result<Image, AnyError> { let mut file = File::open(&path) .with_context(|_| format!("Couldn't open file {}", path.as_ref().display()))?; let mut data = Vec::new(); file.read_to_end(&mut data) .with_context(|_| format!("File {} couldn't be read", path.as_ref().display()))?; let image = Image::parse(data) .with_context(|_| format!("Image in file {} is corrupt", path.as_ref().display()))?; Ok(image) }
Note that the type of the error produced by any of these methods doesn't carry any useful information. Therefore this library should be used only at places where the error is meant for printing out to user or some other handling in uniform manner. Libraries providing building blocks might want to implement their own typed errors, with possibly usefully typed layers.
Consuming the errors
These kinds of errors are usually meant for the user. The outer layer's Display
contains
only its own context, eg:
let inner = std::io::Error::last_os_error(); let outer = inner.context("Some error"); assert_eq!("Some error", outer.to_string());
If you're interested in all the layers, they can be iterated (this simply calls the
source
method until it gets to the bottom).
let inner = std::io::Error::last_os_error(); let outer = inner.context("Some error"); // This will print // Some error // success (or whatever the last io error was) for e in outer.chain() { println!("{}", e); }
Or, more conveniently can be printed as a single string:
let inner = std::io::Error::last_os_error(); let outer = inner.context("Some error"); // Will print something like: // Some error: success println!("{}", outer.display(", "));
Despite being mostly aimed for human output, the chain still can be examined to an extend. In particular, it is possible to look for the outermost error in the chain of specific type. This will find the inner error.
let inner = std::io::Error::last_os_error(); let outer = inner.context("Some error"); assert!(outer.find_source::<std::io::Error>().is_some());
Modules
prelude | A module with reexports to wildcard-import all relevant traits. |
Structs
BoxContext | Additional context for boxed errors. |
Chain | An iterator through the error chain. |
Context | Additional level of context, wrapping some error inside itself. |
DisplayChain | A display implementation for formatting separated layers of an error. |
Traits
BoxedErrorExt | An equivalent of |
BoxedResultExt | A |
ErrorExt | Extension trait for the |
ResultExt | Extension traits for results. |
Type Definitions
AnyError | A type alias for boxed errors. |