Thanks for the writeup! This must have taken you quite a while to investigate and test all the options. I'm sure that people will appreciate the hard work!
I've some small corrections and clarifications relating to SNAFU:
It seems to draw from experiences of using failure
I've never used failure myself, due to ideological differences. One of the main ones is that I believe that error types should be able to be compared for equality. I really like testing my error cases via unit tests.
and ships a comparison [to failure]
The comparison in the guide is mostly due to the popularity of failure. If I received similar questions about migrating from other libraries, I'd add guide sections for those too.
Snafu also don't thinks [sic] having shorthands to create errors from strings is very important
In general, yes, I believe that stringly-typed errors are an anti-pattern as it's basically impossible to perform any kind of code inspection of the error. I also have some grand ideas for how to support i18n of the error messages themselves.
stating: "It's unclear what benefit Failure provides here."
That's as compared to just returning a Box<dyn Error>
, which has built-in support for converting from strings.
let config = fs::read(filename).context(Error::OpenConfig { filename })?;
ensure!(id == 42, Error::UserIdInvalid { user_id });
I'm not sure that this will compile — one of the most contentious design decisions about SNAFU is that it generates types called context selectors. These match the naming of the variants, but are distinct types. I believe this example should be
let config = fs::read(filename).context(OpenConfig { filename })?;
ensure!(id == 42, UserIdInvalid { user_id });
The example also takes Path
instead of a &Path
or the flexible impl AsRef<Path>
— this is likely to cause a compiler error.
Snafu provides the following features:
I'd add it also has
- support for futures 0.1 and 0.3
Future
and Stream
.
- support for no-std usecases
Support for backtraces
The application has to opt in to decide how this is implemented. By default, backtraces are inert — a library can add them freely without requiring any user of that library to also compile an additional crate or rely on nightly. The application can then enable a feature flag that determines what snafu::Backtrace
means:
- Internal implementation (currently using the backtrace crate)
- Public implementation using the backtrace crate
- Public implementation using the unstable
std::backtrace::Backtrace
SNAFU tries to stretch really hard to be usable for library implementors and application writers, as well as supporting a wide range of versions (currently back to Rust 1.31 due to the syn/quote dependencies).
But it misses the "print from main"
We've got related issues that would help ease some of that annoyance, mostly waiting on someone to have time to contribute.
snafu
all seem to make use of format-string like interpolation
SNAFU directly passes all of the arguments to write!
, to allow for the full flexibility of the existing formatting infrastructure. This was one of my annoyances with other tools that tried to simplify that for me. The ability to call Path::display
is a big benefit.