I decided to give snafu
a chance, and quickly grew quite tired of how laborious specifying “error context” next to every single ?
is.
I went back to some of my apps using failure
, to see why it didn’t feel as bad, and I quickly realized, that it’s only because in failure
context is optional… and I just don’t ever use it.
There’s something deeply redundant in how both of these libraries deal with context.
Let’s look at the failure documentation example
use failure::ResultExt;
let mut file = File::open(cargo_toml_path).context("Missing Cargo.toml")?;
file.read_to_end(&buffer).context("Could not read Cargo.toml")?;
It immediately strikes me how redundant handling of context here is: the message is almost the same in both .context
calls. If what you’re doing requires N steps, you’ll have to write the same/similiar boring message that many times.
What is being missed here is that there’s just one context. cargo_toml_path
is the core of that context. For all these "context"s not to be so laborious, the operations have to be grouped and code should have to look something like this (pseudo-Rust):
with_err_ctx!({ path: cargo_toml_path }, || {
let mut file = File::open(cargo_toml_path)?;
file.read_to_end(&buffer)?;
});
Moreover, when you think about where would this example code be, it most probably be in some fn read_cargo_manifest_data(path: &Path) -> Result<SomeData>
. A function! A function in which I quite explicitly narrowed down the context with arguments and documentation. So why there’s no error library that uses all that information.
The way I would like things to be is something like (again: pay attention to bigger picture, not details):
/// Read cargo manifest data
///
/// Returns `SomeData` from the manifest
#[ctx("reading data from cargo manifest at {} in {} mode", path.display(), mode]
fn read_cargo_manifest_data(path: &Path, mode: SomeMode) -> Result<SomeData> {
let mut file = File::open(path)?;
file.read_to_end(&buffer)?;
}
And if something fails, I want the error to be something like:
Error: IO Error: File Not Found
when: opening cargo manifest at /home/dpc/someproject/Cargo.toml
when: reading metadata of crate `foobar`
when: updating graph of dependencies
It also seems to me that such contexts might be useful eg. for logging, and also serve as a form of documentation.
I don’t really know what to do with all that thoughts, so I wrote them down and share here. Feel free to comment and dicuss. Maybe someone with more time at hand, could protottype a library like this, or maintainers of existing error-managing crates, think if this could be implemented.