I'm evaluating snafu
as a replacement for quick-error
and came across a couple of ergonomic issues that I thought would be worth mentioning. They are both related to implementing From
such that the error variant is determined by the source error type.
Without context
First, let's consider the case where you want a variant that acts as a wrapper for a source error type with no additional context. For example, let's say you want an error type
pub enum MyError {
Foo(FooError),
Bar(BarError),
}
This represents an error that is created from either a source error of type FooError
or a source error of type BarError
with no additional context information. quick-error
handles this nicer than snafu
in two ways:
-
It allows using a tuple variant (Foo(FooError)
) instead of requiring a struct variant (Foo { source: FooError }
).
-
It makes it easy to create impl From<FooError> for MyError
and impl From<BarError> for MyError
. (quick-error
makes this easy by having the user write from()
to specify that they want a From
implementation to be created.)
This makes it possible to write
// using quick-error
fn my_fn() -> Result<(), MyError> {
returns_result_with_foo_error()?;
Ok(())
}
instead of having to explicitly specify the variant with
// using snafu
fn my_fn() -> Result<(), MyError> {
returns_result_with_foo_error().context(Foo)?;
Ok(())
}
Item 1 isn't critical, but it would be nice to have. (It looks like discussion about this is in #61, so I won't talk about it more here.)
Item 2 is pretty important so that ?
works without having to call .context()
. I suppose you could argue that the user should always call .context()
to explicitly specify the error variant, but when the variant can be determined directly from the source error type, IMO calling .context()
is unnecessarily verbose.
With context
A related issue is when the error variant does have context information but the error variant can still be determined from the source error type. For example,
pub enum MyError {
Foo {
message: String,
source: FooError,
},
Bar {
message: String,
source: BarError,
},
}
Similar to the case without context, it would be nice to be able to use the ?
operator without explicitly specifying the error variant. So, it would be nice to be able to write
// using quick-error
fn my_fn() -> Result<(), MyError> {
returns_result_with_foo_error().context("message")?;
Ok(())
}
instead of
// using snafu
fn my_fn() -> Result<(), MyError> {
returns_result_with_foo_error().context(Foo { message: "message"})?;
Ok(())
}
To do this, we need an easy way to create impl<C: Into<String>> From<Context<FooError, C>> for MyError
(or just impl<'a> From<Context<FooError, &'a str>> for MyError
).
quick-error
makes this easy by allowing the user to write context(message: &'a str, source: FooError) -> (message.to_string(), source)
to specify that they want a From<Context<&'a str, FooError>>
implementation to be created [note: quick-error
swaps the type parameters of Context
].
Edit: By the way, I wanted to mention that your documentation is awesome!