I have two functions that are similar but can fail with different errors:

#[derive(Debug, thiserror::Error)]
enum MyError {
  #[error("error a")]
  MyErrorA,
  #[error("error b")]
  MyErrorB,
  #[error("bad value ({0})")]
  MyErrorCommon(String),
}

fn functionA() -> Result<String, MyError> {
  // can fail with MyErrorA MyErrorCommon
  todo!()
}

fn functionB() -> Result<String, MyError> {
  // can fail with MyErrorB MyErrorCommon
  todo!()
}

Is there an elegant (*) way I can express this?

If I split the error type into two separate types, is there a way to reuse the definition of MyErrorCommon?


(*) by “elegant” I mean something that improves the code - I’m sure one could define a few macros and solve that way, but I don’t want to go there

edit: grammar (rust grammar)

  • TehPers@beehaw.org
    link
    fedilink
    English
    arrow-up
    4
    ·
    2 days ago

    You can’t create a subset of an enum directly, but splitting this up into multiple types works. You can have FunctionAError with errors that function can produce and a variant for your common errors, and FunctionBError which is similar:

    #[derive(Debug, Error)]
    enum MyErrorCommon {
        #[error("bad value ({0})")]
        MyErrorCommon(String),
    }
    
    #[derive(Debug, Error)]
    enum FunctionAError {
        #[error("error a")]
        MyErrorA,
        Common(#[from] MyErrorCommon),
    }
    
    // and same for FunctionBError
    

    The try operator (?) will automatically use From impls to convert errors for you as well. If a function returns a result containing MyErrorCommon in your function and you use ? on it, it gets converted to that function’s error type for you. thiserror generates the From impl for you if you use #[from].