创建Rust错误填充程序的惯用方式是什么?

时间:2019-12-08 22:10:44

标签: error-handling rust

我有两种在外部包装箱中定义的错误类型。我们称它们为foo::Errorbar::Error。由于它们都是在外部定义的,因此我无法在两者之间直接建立FromInto转换。

在我自己的箱子中,我正在使用实现bar的特征,该特征期望返回值为Result<T, bar::Error>。在实现方法中,我从foo调用函数,这些函数返回许多Result<T, foo::Error>类型。

我想将这些foo::Error错误映射到bar::Error错误,这样我的特征实现就很整齐,但是我能做的最好的事情是创建一个单独的填充错误,然后再笨拙.map_err(ShimErr::from)?。如果我在实现中调用许多foo::函数,这可能会使我的代码混乱。

use foo;
use bar;

struct ShimErr(foo::Error);

impl From<foo::Error> for ShimErr {
    fn from(e: foo::Error) -> Self {
        Self(e)
    }
}

impl From<ShimErr> for bar::Error {
    fn from(e: ShimErr) -> Self {
        Self{}
    }
}

struct MyTraitImpl {}

impl bar::SomeTrait for MyTraitImpl {
    fn do_something() -> Result<i32, bar::Error> {
        // FIXME: THIS IS CLUNKY
        let foo_val: i32 = foo::call_something().map_err(ShimErr::from)?;
        Ok(foo_val * 2)
    }
}

有更好的方法吗?在理想的世界中,将有一种方法可以只使用?

如果我删除了map_err并尝试直接使用?

error[E0277]: `?` couldn't convert the error to `bar::Error`
  --> shimtest.rs:32:49
   |
32 |         let foo_val: i32 = foo::call_something()?;
   |                                                 ^ the trait `std::convert::From<foo::Error>` is not implemented for `bar::Error`
   |
   = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
   = help: the following implementations were found:
             <bar::Error as std::convert::From<ShimErr>>
   = note: required by `std::convert::From::from`

1 个答案:

答案 0 :(得分:-1)

我处理与其他装箱不同的多个错误的方式是制作自己的Error枚举,并直接使用它代替其他错误。

因此,可以创建一个既包含foo::Error也包含foo::Error的枚举,而不是只包含bar::Error的元组结构。之后,您可以使用?将所有错误转换为自定义枚举错误。

在某些情况下,我认为您仍然偶尔需要使用类似.map_err(Into::into)的东西,但通常仅当返回的变量是带有外部错误的Result时。

use bar::Error as BarError;
use foo::Error as FooError;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::result::Result as StdResult;

/// Common result type that uses the custom error
pub type Result<T> = StdResult<T, Error>;

/// Common error type used as a holder for errors from various other libraries.
#[derive(Debug)]
pub enum Error {
    Bar(BarError),
    Foo(FooError),
}

impl From<BarError> for Error {
    fn from(err: BarError) -> Error {
        Error::Bar(err)
    }
}

impl From<FooError> for Error {
    fn from(err: FooError) -> Error {
        Error::Foo(err)
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            Error::Bar(ref inner) => inner.fmt(f),
            Error::Foo(ref inner) => inner.fmt(f),
        }
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Bar(ref inner) => inner.description(),
            Error::Foo(ref inner) => inner.description(),
        }
    }
}

现在,您可以按如下方式使用自定义ResultError

// this Result is now our custom Result type with custom error
// same as std::result::Result<i32, Error> (with Error as our custom error)
fn do_something() -> Result<i32> {
    let foo_val: i32 = foo::call_something()?; // should convert to our custom error
    Ok(foo_val * 2)
}

fn do_something_else() -> Result<i32> {
    // directly returning a Result<> with a different error type will need map_err
    foo::call_something().map_err(Into::into)
}