Skip to content

How to combine errors? #323

@bryanlarsen

Description

@bryanlarsen

The documentation talks about splitting a source error into two separate errors. In my experience the converse also often happens, sometimes you want to combine several source errors into a single one. However as a newbie, I've been unable to make this happen. I've attached an MRE of one attempt, but I'm not necessarily asking for how to make my MRE compile -- something completely different that matches my use case would be awesome. For example, I use the InnerError pattern and source(false) but a less verbose solution that doesn't need one or both of those likely would be even better.

use rand::Rng;
use snafu::prelude::*;

#[derive(Debug, Snafu)]
pub enum Error {
    /// Error in the construction of the NFT
    #[snafu(display("Parse Error on {field}: {source}"))]
    ParseError {
        field: String,
        #[snafu(source(false))]
        source: InnerParseError,
    },
}

#[derive(Debug, Snafu)]
pub enum InnerParseError {
    #[snafu(display("YAMLError {source}"))]
    YamlError {
        #[snafu(source(false))]
        source: serde_yaml::Error,
    },

    #[snafu(display("JSONError {source}"))]
    JsonError {
        #[snafu(source(false))]
        source: serde_json::Error,
    },

    #[snafu(display("{message}"))]
    MessageError { message: String },
}

fn main_wrapped() -> Result<String, Error> {
    let r: i32 = rand::thread_rng().gen_range(0..3);

    Ok(match r {
        0 => {
            let _ = serde_yaml::from_str("invalid yaml").with_context(|e| ParseSnafu {
                field: "0",
                source: YamlSnafu { source: e },
            })?;
            "0".to_string()
        }
        1 => {
            let _ = serde_json::from_str("invalid json").with_context(|e| ParseSnafu {
                field: "0",
                source: JsonSnafu { source: e },
            })?;
            "1".to_string()
        }
        _ => {
            ensure!(
                false,
                ParseSnafu {
                    field: "2",
                    source: InnerParseError::MessageError {
                        message: "blah".to_owned()
                    }
                }
            );
            "2".to_string()
        }
    })
}

fn main() {
    match main_wrapped() {
        Ok(s) => {
            panic!("#{s:?}");
        }
        Err(e) => {
            println!("#{e:?}");
        }
    }
}
[package]
name = "mre"
version = "0.1.0"
edition = "2021"

[dependencies]
rand = "0.8"
serde_yaml = "0.8"
serde_json = "1"
snafu = "0.7"

/home/blarsen/.cargo/bin/cargo run --manifest-path /work/gitnft/mre/Cargo.toml 
   Compiling mre v0.1.0 (/work/gitnft/mre)
error[E0271]: type mismatch resolving `<ParseSnafu<&str, YamlSnafu<&mut serde_yaml::Error>> as IntoError<Error>>::Source == serde_yaml::Error`
   --> src/main.rs:36:58
    |
36  |             let _ = serde_yaml::from_str("invalid yaml").with_context(|e| ParseSnafu {
    |                                                          ^^^^^^^^^^^^ expected struct `serde_yaml::Error`, found struct `NoneError`
    |
note: required by a bound in `snafu::ResultExt::with_context`
   --> /home/blarsen/.cargo/registry/src/github.com-1ecc6299db9ec823/snafu-0.7.0/src/lib.rs:529:26
    |
529 |         C: IntoError<E2, Source = E>,
    |                          ^^^^^^^^^^ required by this bound in `snafu::ResultExt::with_context`

error[E0277]: the trait bound `InnerParseError: From<YamlSnafu<&mut serde_yaml::Error>>` is not satisfied
   --> src/main.rs:36:71
    |
36  |               let _ = serde_yaml::from_str("invalid yaml").with_context(|e| ParseSnafu {
    |  __________________________________________________________------------_^
    | |                                                          |
    | |                                                          required by a bound introduced by this call
37  | |                 field: "0",
38  | |                 source: YamlSnafu { source: e },
39  | |             })?;
    | |_____________^ the trait `From<YamlSnafu<&mut serde_yaml::Error>>` is not implemented for `InnerParseError`
    |
    = note: required because of the requirements on the impl of `Into<InnerParseError>` for `YamlSnafu<&mut serde_yaml::Error>`
note: required because of the requirements on the impl of `IntoError<Error>` for `ParseSnafu<&str, YamlSnafu<&mut serde_yaml::Error>>`
   --> src/main.rs:4:17
    |
4   | #[derive(Debug, Snafu)]
    |                 ^^^^^
note: required by a bound in `snafu::ResultExt::with_context`
   --> /home/blarsen/.cargo/registry/src/github.com-1ecc6299db9ec823/snafu-0.7.0/src/lib.rs:529:12
    |
529 |         C: IntoError<E2, Source = E>,
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `snafu::ResultExt::with_context`
    = note: this error originates in the derive macro `Snafu` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0271]: type mismatch resolving `<ParseSnafu<&str, JsonSnafu<&mut serde_json::Error>> as IntoError<Error>>::Source == serde_json::Error`
   --> src/main.rs:43:58
    |
43  |             let _ = serde_json::from_str("invalid json").with_context(|e| ParseSnafu {
    |                                                          ^^^^^^^^^^^^ expected struct `serde_json::Error`, found struct `NoneError`
    |
note: required by a bound in `snafu::ResultExt::with_context`
   --> /home/blarsen/.cargo/registry/src/github.com-1ecc6299db9ec823/snafu-0.7.0/src/lib.rs:529:26
    |
529 |         C: IntoError<E2, Source = E>,
    |                          ^^^^^^^^^^ required by this bound in `snafu::ResultExt::with_context`

error[E0277]: the trait bound `InnerParseError: From<JsonSnafu<&mut serde_json::Error>>` is not satisfied
   --> src/main.rs:43:71
    |
43  |               let _ = serde_json::from_str("invalid json").with_context(|e| ParseSnafu {
    |  __________________________________________________________------------_^
    | |                                                          |
    | |                                                          required by a bound introduced by this call
44  | |                 field: "0",
45  | |                 source: JsonSnafu { source: e },
46  | |             })?;
    | |_____________^ the trait `From<JsonSnafu<&mut serde_json::Error>>` is not implemented for `InnerParseError`
    |
    = note: required because of the requirements on the impl of `Into<InnerParseError>` for `JsonSnafu<&mut serde_json::Error>`
note: required because of the requirements on the impl of `IntoError<Error>` for `ParseSnafu<&str, JsonSnafu<&mut serde_json::Error>>`
   --> src/main.rs:4:17
    |
4   | #[derive(Debug, Snafu)]
    |                 ^^^^^
note: required by a bound in `snafu::ResultExt::with_context`
   --> /home/blarsen/.cargo/registry/src/github.com-1ecc6299db9ec823/snafu-0.7.0/src/lib.rs:529:12
    |
529 |         C: IntoError<E2, Source = E>,
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `snafu::ResultExt::with_context`
    = note: this error originates in the derive macro `Snafu` (in Nightly builds, run with -Z macro-backtrace for more info)

Some errors have detailed explanations: E0271, E0277.
For more information about an error, try `rustc --explain E0271`.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions