【问题标题】:How to implement From trait for custom error types?如何为自定义错误类型实现 From 特征?
【发布时间】:2019-09-15 09:43:44
【问题描述】:

我目前正在尝试为我的 CLI 应用程序编写自定义错误类型。现在我想编写一个From trait 的实现,这样我的自定义错误类型就可以包含所有可能发生的第三方库错误。

错误枚举:

#[derive(Debug)] // Allow the use of "{:?}" format specifier
pub enum CustomError {
    Git(git2::Error),
    Other
}

现在我想为git2 库中的git2::Error 实现From 特征,以便在我的函数中使用? 运算符。

impl From<(git2::Error)> for CustomError {
    fn from(cause: git2::Error) -> Self {
        CustomError::Git(cause)
    }
}

但是当我尝试使用我的自定义错误来映射这样的错误时:

let repo = Repository::open(path).map_err(|err| CustomError::Git)?;

我收到以下错误消息:

the trait `std::convert::From<fn(git2::error::Error) -> error::CustomError {error::CustomError::Git}>` is not implemented for `error::CustomError `

谁能帮我理解为什么会出现这个错误以及如何解决这个问题?

感谢任何帮助

【问题讨论】:

    标签: error-handling rust


    【解决方案1】:

    你混淆了一大堆概念;让我们看看我们是否可以一起解决这个问题,并希望能澄清所有这些。

    git2 crate 有它自己的错误类型,你肯定已经发现了。您对自定义错误的定义也很好。

    问题是双重的:

    1. 你对From&lt;_&gt;的实现

    From&lt;E&gt; 允许您通过提供转换功能 (from()) 将一种类型从一种类型转换为另一种类型。

    您的实现如下:

    impl From<(git2::Error)> for CustomError {
        fn from(cause: git2::Error) -> Self {
            CustomError::Git(cause)
        }
    }
    

    rust 中的括号没有添加到不应该添加的位置,而这正是这种情况之一。通过这样做,您实际上定义了From&lt;(T)&gt;,而不是From&lt;T&gt;。这是错误 #1。

    正确的实现只是去掉括号:

    impl From<git2::Error> for CustomError {
        fn from(cause) -> Self {
            CustomError::Git(cause)
        }
    }
    
    1. 您的实际转化率

    本身不是错误,而是完全不必要的操作,因为 ? 运算符会为您处理它。不需要map_err(),如果有,你会使用into(),而不是硬调用类型(它应该已经在你的函数中定义为类型)。

    请记住,转换特征的全部意义在于定义它们因此您不必显式调用它们

    按工作顺序的代码的最终“演示”版本可能如下所示:

    extern crate git2;
    use git2::Repository;
    
    #[derive(Debug)] // Allow the use of "{:?}" format specifier
    pub enum CustomError {
        Git(git2::Error),
        Other
    }
    impl From<(git2::Error)> for CustomError {
        fn from(cause: git2::Error) -> Self {
            CustomError::Git(cause)
        }
    }
    fn test() -> Result<(), CustomError> {
        let path = "foo";
        let output = Repository::open(path)?;
        Ok(())
    }
    fn main() {
        println!("Hello, world!");
    }
    

    【讨论】:

    • 非常感谢您非常全面的回答和解释。现在一切都变得更有意义了。这是否也适用于转换 ok_or 中的错误?
    • 只要编译器期望(由于类型)您的自定义错误类型但以不同的东西开始 - 它会检查它是否可以转换。
    猜你喜欢
    • 2020-03-31
    • 1970-01-01
    • 2015-10-08
    • 1970-01-01
    • 1970-01-01
    • 2020-05-05
    • 2019-12-08
    • 2016-06-01
    • 2015-12-02
    相关资源
    最近更新 更多