合并两种错误类型的最惯用方式是什么?

时间:2018-09-21 07:47:24

标签: error-handling rust idiomatic

我有一个类型Foo,其方法可能会“引发”相关类型Foo::Err的错误。

pub trait Foo {
    type Err;

    fn foo(&mut self) -> Result<(), Self::Err>;
}

我还具有另一个特征Bar,该特征旨在处理FooBar可能会发出自己的错误(由关联的类型Bar::Err指定),但也可能会遇到由其处理的Foo生成的错误。

我可以看到两种处理方法,但我不知道哪种方法最适合Rust。

第一个将结果嵌入结果:

pub trait Bar1 {
    type Err;

    fn bar<F: Foo>(&mut self, foo: F) -> Result<Result<F, F::Err>, Self::Err>;
}

第二个错误将两种错误类型合并为一个专用的枚举:

pub trait Bar2 {
    type Err;

    fn bar<F: Foo>(&mut self, foo: F) -> Result<F, Choice<F::Err, Self::Err>>;
}

第二个在语义上看起来更简洁,但为处理其他枚举创建了一些障碍。

playground

2 个答案:

答案 0 :(得分:2)

您应该使用特征对象Error,然后返回遇到的第一个错误:

pub trait Bar {
    fn bar<F: Foo>(&mut self, foo: F) -> Result<F, Box<dyn Error>>;
}

或像这样实现您的特质:

impl Bar for MyType {
    type Err = Box<dyn Error>;

    fn bar<F: Foo>(&mut self, foo: F) -> Result<F, Self::Err>;
}

如果您确实要遇到两个错误(但这很奇怪,因为一个错误足以使该过程无法正常进行),您可以使用failure之类的条板箱来创建“错误跟踪”。

作为一般建议,您不应忘记使用std中的特征为代码添加更多语义。

答案 1 :(得分:1)

通常,您不执行“合并”,而是使用嵌套错误,例如。

enum IntError {
    Overflow,
    Underflow
}

enum StrError {
    TooLong,
    TooShort,
}

enum GenericError {
    Int(IntError),
    Str(StrError),
}

impl From<IntError> for GenericError {
    fn from(e: IntError) -> Self {
        GenericError::Int(e)
    }
}

impl From<StrError> for GenericError {
    fn from(e: StrError) -> Self {
        GenericError::Str(e)
    }
}