Rust 正确的错误处理(使用问号自动从一种错误类型转换为另一种错误类型)

Rust 正确的错误处理(使用问号自动从一种错误类型转换为另一种错误类型),rust,Rust,我想学习如何正确处理Rust中的错误。我已经读过和;现在我想知道如何处理此函数中的错误: fn get_synch_point(&self) -> Result<pv::synch::MeasPeriods, reqwest::Error> { let url = self.root.join("/term/pv/synch"); // self.root is url::Url let url = match url { Ok(url)

我想学习如何正确处理Rust中的错误。我已经读过和;现在我想知道如何处理此函数中的错误:

fn get_synch_point(&self) -> Result<pv::synch::MeasPeriods, reqwest::Error> {
    let url = self.root.join("/term/pv/synch"); // self.root is url::Url
    let url = match url {
        Ok(url) => url,
        // ** this err here is url::ParseError and can be converted to Error::Kind https://docs.rs/reqwest/0.8.3/src/reqwest/error.rs.html#54-57 **//
        Err(err) => {
            return Err(Error {
                kind: ::std::convert::From::from(err),
                url: url.ok(),
            })
        }
    };

    Ok(reqwest::get(url)?.json()?) //this return reqwest::Error or convert to pv::sych::MeasPeriods automaticly
}      
处理该案件的适当模式是什么?对我来说,在这种情况下,
reqwest::Error
是一个很好的解决方案,因此我希望避免定义自己的错误类型:

enum MyError {
    Request(reqwest::Error),
    Url(url::ParseError) // this already a part of request::Error::Kind!!!
} 

不幸的是,在您的情况下,如果
reqwest
库没有提供这样做的方法(很可能没有),您就无法从其他错误类型创建
reqwest::Error
。要解决这个非常常见的问题,特别是在使用多个库的应用程序中,正确的解决方案应该是以下之一:

  • 使用应用程序处理的所有错误(或应用程序的一个子系统;粒度高度取决于项目)声明您自己的自定义枚举,并声明从您处理的所有错误到此枚举类型的转换

    作为这种方法的扩展,您可以使用(或者,错误链基本上基于错误链)以半自动的方式生成这样的自定义类型和转换

  • 使用特殊的通用错误类型。基本上有两种:

    a<代码>框在标准库中定义的位置

    b。使用板条箱中定义的类型

    然后问号操作符将能够将任何兼容的错误转换为这些类型之一,因为各种
    实现将
    转换为

  • 请注意,
    故障
    板条箱旨在定义Rust社区中推广的错误。它不仅提供了一个常见的错误类型和特征(它修复了
    std::error::error
    trait的各种问题;请参见示例),还提供了定义自己的错误类型(例如,使用)的工具,以及跟踪错误上下文、原因和生成回溯的工具。此外,它试图与现有的错误处理方法尽可能兼容,因此可以使用它与使用其他较旧方法的库集成(
    std::error::error
    错误链
    快速错误
    )。所以我强烈建议你在其他选项之前先考虑使用这个箱子。

    我已经开始在我的应用程序项目中使用
    failure
    ,我无法表达错误处理变得多么容易和更好。我的做法如下:

  • 定义
    结果
    类型:

    type Result<T> = std::result::Result<T, failure::Error>;
    
    type Result=std::Result::Result;
    
  • 在可以返回错误的任何地方使用
    Result
    ,使用问号运算符(
    )在错误和函数之间进行转换,例如或创建我自己的错误消息

  • 我还没有使用
    failure
    编写一个库,但我认为对于库来说,创建声明为枚举的更具体的错误是很重要的,这可以通过
    failure\u-deriver
    板条箱来完成。但是,对于应用程序来说,
    failure::Error
    类型已经足够了。

    As,板条箱应该是您的起点。以下是我的解决方案:

    use std::io;
    use std::result;
    
    use failure::{Backtrace, Fail};
    
    /// This is a new error type manged by Oxide library.
    /// The custom derive for Fail derives an impl of both Fail and Display.
    #[derive(Debug, Fail)]
    pub enum OxideError {
        #[fail(display = "{}", message)]
        GeneralError { message: String },
    
        #[fail(display = "{}", message)]
        IoError {
            message: String,
            backtrace: Backtrace,
            #[cause]
            cause: io::Error,
        },
    }
    
    /// Create general error
    pub fn general(fault: &str) -> OxideError {
        OxideError::GeneralError {
            message: String::from(fault),
        }
    }
    
    /// Create I/O error with cause and backtrace
    pub fn io(fault: &str, error: io::Error) -> OxideError {
        OxideError::IoError {
            message: String::from(fault),
            backtrace: Backtrace::new(),
            cause: error,
        }
    }
    

    此错误枚举是可扩展的,允许它适应将来可能对程序进行的修改。

    在这种情况下,无法重用底层错误类型,因为无法构造其隐藏字段。即使在可能的情况下,我也会建议您不要这样做,以便使您的代码更灵活,更能经得起未来的考验

    定义自定义错误类型可能需要编写大量样板文件,但幸运的是,有几个库可以减轻这种痛苦。上面已经提到了失败、错误链和快速错误,但我想向您指出我写的一个板条箱,其中包含的样板文件比其他板条箱更少:。有了它,你可以写:

    #[macro_use] extern crate custom_error;
    
    custom_error!{ MyError
        Request{source: reqwest::Error} = "request error",
        Url{source: url::ParseError}    = "invalid url"
    }
    
    更新2020 rust编程语言正在快速发展,因此可以添加新的答案!我真的很喜欢,但现在我想我会是我的爱人

    使用thisrerror::Error;
    #[导出(错误,调试)]
    发布枚举数据存储错误{
    #[错误(“数据存储已断开”)]
    断开连接(#[从]io::错误),
    #[错误(“键`{0}`的数据不可用”)]
    编校(字符串),
    #[错误(“无效标头(应为{应为:?},已找到{已找到:?})”)]
    无效标题{
    应为:字符串,
    找到:字符串,
    },
    #[错误(“未知数据存储错误”)]
    不详,
    }
    
    这允许将
    io::Error
    更改为
    DataStoreError::断开带有问号的
    。到这里来

    有用链接:


    其他有趣的板条箱:
    • -基于std::Error::Error构建的灵活具体错误类型
    • -情况正常:所有问题都已解决-SNAFU是一个库,可以在添加上下文时轻松地将底层错误分配到特定于域的错误中。(类似于此错误)
    • -此板条箱包含一个,该宏可以更轻松地定义自定义错误,而无需编写大量样板代码
    对于恐慌:
    • -此板条箱旨在使proc宏中的错误报告简单易用
    • -人类的恐慌信息。通过调用std::panic::set_hook来处理恐慌,从而使错误对人类有利

    Oo。谢谢你的板条箱。我第一次使用failure,但现在我只想使用standard trait Error,这个宏非常简单!这是最多的投票,但失败和错误链增加了额外的复杂和非标准特征。如今,有些板条箱只能进行标准的防锈错误处理,例如:和。你能把它们也包括在这里吗?“失败”已经被弃用了无论如何,“更容易”和“thiserror”(更精确,就像图书馆一样)是现代的首选选择。两者都由同一作者编写,可以一起使用