shepmaster / snafu

Easily assign underlying errors into domain-specific errors while adding context
https://docs.rs/snafu/
Apache License 2.0
1.4k stars 60 forks source link

Creating a `whatever` error value #387

Open jondot opened 1 year ago

jondot commented 1 year ago

So far the proper shorthand for creating the value (but not returning immediately) was something along:

Err(FromString::without_source(
    "msg".into(),
))

is there a shorter, nicer way?

Enet4 commented 1 year ago

I'm afraid that that would be the best you can do if you want to use snafu::Whatever. But in this case you might be better served with your own error type, with support for whatever.

#[derive(Debug, Snafu)]
enum Error {
    #[snafu(whatever, display("{message}"))]
    Generic {
        message: String,
        #[snafu(source(from(Box<dyn std::error::Error>, Some)))]
        source: Option<Box<dyn std::error::Error>>,
    },
}

You can then use the selector GenericSnafu, or create your own constructor associated function.

GenericSnafu { message: "msg" }.fail()

This supports the various integrations with whatever errors, including the whatever! macro and the extension methods whatever_context and with_whatever_context, while already serving as a baseline for making the error variants more specific in the future.

jondot commented 1 year ago

@Enet4 maybe one more question, I'm looking for a way to create a Whatever like error, but with one more context field added, which is, in my Error type, have 2 whatever errors. One with the message, and one with a message + extra info. I want the whatever semantics, because I want to box the previous error generically without really caring about its type, as long as I can convert it fluently.

Enet4 commented 1 year ago

I want the whatever semantics, because I want to box the previous error generically without really caring about its type, as long as I can convert it fluently.

As far as I know, that is not whatever-specific. Any error type or variant may have a dynamic source error.

#[derive(Debug, Snafu)]
enum Error {
    #[snafu(whatever, display("{message}"))]
    Generic {
        message: String,
        #[snafu(source(from(Box<dyn std::error::Error>, Some)))]
        source: Option<Box<dyn std::error::Error>>,
    },
    #[snafu(display("Failed with code {code}: {message}"))]
    WithCode {
        message: String,
        code: i32,
        #[snafu(source(from(Box<dyn std::error::Error>, Some)))]
        source: Option<Box<dyn std::error::Error>>,
    },
}
shepmaster commented 1 year ago

is there a shorter, nicer way?

I can't think of any technical reason that snafu::Whatever could not add an implementation of From<String> (or &str or AsRef<str>, etc. whatever works well).

Then that would be

Whatever::from("msg")
jondot commented 1 year ago

I want the whatever semantics, because I want to box the previous error generically without really caring about its type, as long as I can convert it fluently.

As far as I know, that is not whatever-specific. Any error type or variant may have a dynamic source error.

#[derive(Debug, Snafu)]
enum Error {
    #[snafu(whatever, display("{message}"))]
    Generic {
        message: String,
        #[snafu(source(from(Box<dyn std::error::Error>, Some)))]
        source: Option<Box<dyn std::error::Error>>,
    },
    #[snafu(display("Failed with code {code}: {message}"))]
    WithCode {
        message: String,
        code: i32,
        #[snafu(source(from(Box<dyn std::error::Error>, Some)))]
        source: Option<Box<dyn std::error::Error>>,
    },
}

yes, this is exactly what I did, and I also have to have something like this in place.

For some reason I have some typing hell making it work for all the cases. Will give it another try.

Enet4 commented 1 year ago

I also have to have something like this in place.

That could bring some issues. Snafu should take care of converting the source for you. Too many calls to Into::into or From::from and the compiler might not be able to infer which intermediate types are expected to be used.

jondot commented 1 year ago

Since I also have Option:

    #[snafu(display("provider error while accessing `{}`: `{}`", path, msg))]
    ProviderSourceError {
        path: String,
        msg: String,
        #[snafu(source(from(Box<dyn std::error::Error + Send + Sync>, Some)))]
        source: Option<Box<dyn std::error::Error + Send + Sync>>,
    },

I'm getting lots of incompatibilities. I guess Option screws this up, but this is similar to what the whatever variant is using, so maybe whatever macro does some magic here