Replies: 9 comments
-
Snafu can (and probably should) take care of encasing the source error for you. enum Error {
Cache {
message: String,
#[snafu(source(from(Box<dyn std::error::Error + Send + Sync>))]
source: Box<dyn std::error::Error + Send + Sync>,
},
} With this you should only need to write this: do_things().context(CacheSnafu{ message })?; Error transformation is documented here. This also works if you set up more specific source error types, which would be more typical and more aligned with the design of Snafu errors. |
Beta Was this translation helpful? Give feedback.
-
Tried that, but looks like
|
Beta Was this translation helpful? Give feedback.
-
Hmm, it's probably missing the constructor parameter, enum Error {
Cache {
message: String,
#[snafu(source(from(Box<dyn std::error::Error + Send + Sync>, Box))]
source: Box<dyn std::error::Error + Send + Sync>,
},
} |
Beta Was this translation helpful? Give feedback.
-
Yep tried putting a
|
Beta Was this translation helpful? Give feedback.
-
There's probably a missing formula for turning an arbitrary error into |
Beta Was this translation helpful? Give feedback.
-
Yea, I saw some discussion about error mapping
|
Beta Was this translation helpful? Give feedback.
-
That's the usual error text for having a use std::error::Error;
fn demo(e: Box<dyn Error>) -> impl Error {
e
}
Instead, you'd want something like: use snafu::prelude::*;
#[derive(Debug, Snafu)]
struct OuterError {
#[snafu(source(from(InnerError, Box::new)))]
source: Box<dyn snafu::Error>,
}
#[derive(Debug, Snafu)]
struct InnerError; Unfortunately, you won't be able to add a second
Under the hood, SNAFU generates some code like impl ::snafu::IntoError<OuterError> for OuterSnafu
where
OuterError: ::snafu::Error + ::snafu::ErrorCompat,
{
type Source = InnerError;
#[track_caller]
fn into_error(self, error: Self::Source) -> OuterError {
let error: Box<dyn snafu::Error> = (Box::new)(error);
OuterError { source: error }
}
} This maps a (source error, context selector) to a (output error).
You'd need to be able to write code like impl<E> ::snafu::IntoError<OuterError> for OuterSnafu
where
E: ::snafu::Error,
{
type Source = E; The problem with that is that you run into
I don't see how to implement that trait for this case. For the case of multiple Something that you could do today would be to add this to your code trait ResultExt<T, E> {
fn boxed(self) -> Result<T, Box<dyn snafu::Error + 'static>>
where
E: snafu::Error + 'static;
}
impl<T, E> ResultExt<T, E> for Result<T, E> {
fn boxed(self) -> Result<T, Box<dyn snafu::Error + 'static>>
where
E: snafu::Error + 'static,
{
self.map_err(|e| Box::new(e) as Box<dyn snafu::Error>)
}
} You would then call #[derive(Debug, Snafu)]
struct OuterError {
source: Box<dyn snafu::Error>,
}
#[derive(Debug, Snafu)]
struct Inner1Error;
#[derive(Debug, Snafu)]
struct Inner2Error;
fn inner1() -> Result<(), Inner1Error> {
Ok(())
}
fn outer1() -> Result<(), OuterError> {
inner1().boxed().context(OuterSnafu)
}
fn inner2() -> Result<(), Inner1Error> {
Ok(())
}
fn outer2() -> Result<(), OuterError> {
inner2().boxed().context(OuterSnafu)
}
fn meta_outer() -> Result<(), OuterError> {
outer1()?;
outer2()?;
Ok(())
} We could probably even add that #123 may be related. |
Beta Was this translation helpful? Give feedback.
-
That would look roughly like this trait IntoError<S, C> {
fn into_error(source: S, context: C) -> Self;
}
// ---------- Two specific sources, one context
// error1().context(AlphaSnafu)?;
// error2().context(AlphaSnafu)?;
struct S1;
struct S2;
struct C1;
struct OuterError;
impl IntoError<S1, C1> for OuterError {
fn into_error(source: S1, context: C1) -> Self {
OuterError // Would have conversion logic here
}
}
impl IntoError<S2, C1> for OuterError {
fn into_error(source: S2, context: C1) -> Self {
OuterError // Would have conversion logic here
}
}
// ---------- Any possible error (using Display as a placeholder)
use std::fmt;
struct C2;
struct AbsorbingError(String);
impl<S> IntoError<S, C2> for AbsorbingError
where
S: fmt::Display,
{
fn into_error(source: S, context: C2) -> Self {
AbsorbingError(source.to_string())
}
}
impl fmt::Display for AbsorbingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
"Absorbing".fmt(f)
}
} |
Beta Was this translation helpful? Give feedback.
-
Thanks for this very thorough explanation! |
Beta Was this translation helpful? Give feedback.
-
Hi, I'd like to consolidate error types, meaning, say I have:
And I'd like my crate, which hides the various providers, emit just the following errors:
where each of these can but not must also include a source.
So far what I'm doing is:
but I feel it's too much friction.
first note i know
Whatever
maps everything and can be used, but I feel it's too much "catch all", and I can't have more than 1Whatever
in a single enum derived with Snafu.second note, i might as well just do this, in which case I don't see how Snafu is helping me save my efforts, maybe only for display and such:
Any elegant solution for this?
Beta Was this translation helpful? Give feedback.
All reactions