• jenesaisquoi@feddit.org
    link
    fedilink
    English
    arrow-up
    3
    ·
    edit-2
    1 day ago

    Check out https://docs.rs/thiserror/latest/thiserror/. You can do this:

    #[derive(Debug, thiserror::Error)]
    pub enum MyError {
        #[error("Failed to do the thing, blabla, put something clear here")]
        DoingTheThing(#[from] LibraryError)
    }
    

    And then you can just do foo(a, b)? because there’s now a From impl for MyError from LibraryError.

    You now get a proper error chain via source(). You can use this to have a nice report at the top level using https://docs.rs/thiserror-ext/0.3.0/thiserror_ext/struct.Report.html that looks like this:

    Outer error text
    
    Caused by these errors (recent errors listed first):
      1. Middle error text
      2. Inner error text
    
    • treadful@lemmy.zip
      cake
      link
      fedilink
      English
      arrow-up
      2
      ·
      1 day ago

      I’m playing with this pattern, but it seems like it forces you to have a variant for every upstream error. For instance, I have this DatabaseError:

      	#[derive(Clone, Debug, Error)]
      	pub enum DatabaseError {
      	    #[error("database error: {0}")]
      	    Database(String),
      	    #[error("failed to deserialize from DB: {0}")]
      	    Deserialize(String),
      	    #[error("failed to serialize input: {0}")]
      	    Serialize(String),
      	    #[error("unexpected error: {0}")]
      	    Unexpected(String),
      	}
      

      But I might have 2 or 3 error types that turn into DatabaseError::Serialize. Then I end up doing custom From impls. Then I realize serde might be Serialize or Deserialize, so I’m back where I started.

      Though I would like to have that chain of errors, but I haven’t figured out how to do the custom impl From yet (or if it ends up being useful in my case, even).

      Thanks for the ideas.

      • jenesaisquoi@feddit.org
        link
        fedilink
        English
        arrow-up
        2
        ·
        10 hours ago

        For multiple types turning into one variant: you can either impl From for the others yourself, or you can have another enum with variants for them and have thiserror generate the From impl. I usually have lots of enums, at least one per module, sometimes even one per function, that form a tree. The errors you design should relate to the action that was being tried, and only encapsulate the lower error as context information.

        I see you’re using strings. You can do that, but then you’ll lose control over formatting, and additional context info that might be contained in them.

        Designing errors isn’t trivial and requires some experience. Try some things and over time you’ll see what works best.

        You can also check out https://docs.rs/snafu/latest/snafu/guide/index.html, another great library - their docs contain excellent guidance on designing error types.

        • treadful@lemmy.zip
          cake
          link
          fedilink
          English
          arrow-up
          1
          ·
          7 hours ago

          I see you’re using strings. You can do that, but then you’ll lose control over formatting, and additional context info that might be contained in them.

          I just haven’t figured out how to jam multiple error types into one enum variant yet. There’s no reason for the consumer of the module to get more pointless variants, so I’m not really sure I want an enum of enums, but maybe I’m overthinking it. I’ll fiddle more and check out your link later today.