Error handling 如何在Rust中定义自定义的'Error'类型?

Error handling 如何在Rust中定义自定义的'Error'类型?,error-handling,rust,Error Handling,Rust,我正在编写一个函数,它可以返回几个不同错误中的一个 fn foo(...) -> Result<..., MyError> {} 这是最惯用的方式吗?我如何实现错误特性 这是最惯用的方式吗?我如何实现错误特性 是的,这是一种常见的方式。“惯用”取决于您希望错误的类型有多强,以及您希望它如何与其他事物进行互操作 我如何实现错误特性 严格地说,你不需要在这里。您可能需要与其他需要Error的东西进行互操作,但由于您已将返回类型直接定义为此枚举,因此您的代码在没有它的情况下应该可以

我正在编写一个函数,它可以返回几个不同错误中的一个

fn foo(...) -> Result<..., MyError> {}
这是最惯用的方式吗?我如何实现
错误
特性

这是最惯用的方式吗?我如何实现错误特性

是的,这是一种常见的方式。“惯用”取决于您希望错误的类型有多强,以及您希望它如何与其他事物进行互操作

我如何实现错误特性

严格地说,你不需要在这里。您可能需要与其他需要
Error
的东西进行互操作,但由于您已将返回类型直接定义为此枚举,因此您的代码在没有它的情况下应该可以工作。

您的实现与您的实现完全一样;它没有什么特别之处:

pub trait Error: Debug + Display {
    fn description(&self) -> &str { /* ... */ }
    fn cause(&self) -> Option<&Error> { /* ... */ }
    fn source(&self) -> Option<&(Error + 'static)> { /* ... */ }
}
当然,
Thing
包含的内容以及方法的实现在很大程度上取决于您希望出现的错误类型。也许您想在其中包含一个文件名,或者是某种整数。也许您希望使用
enum
而不是
struct
来表示多种类型的错误

如果您最终包装了现有错误,那么我建议实现
From
,以便在这些错误和您的错误之间进行转换。这允许您使用
尝试
并且有一个非常符合人体工程学的解决方案

这是最惯用的方式吗

惯用的说法是,一个库将有一小部分(可能1-3)暴露的主要错误类型。这些可能是其他错误类型的枚举。这使您的板条箱的消费者不必处理各种类型的爆炸。当然,这取决于您的API以及将一些错误集中在一起是否有意义

另一件需要注意的事情是,当您选择在错误中嵌入数据时,可能会产生广泛的影响。例如,标准库在与文件相关的错误中不包含文件名。这样做会增加每个文件错误的开销。方法的调用方通常具有相关上下文,并且可以决定是否需要将该上下文添加到错误中


我建议用手做几次,看看所有的部分是如何结合在一起的。一旦你做到了这一点,你就会厌倦手工操作。然后,您可以查看提供宏以简化样板的板条箱:

我首选的库是SNAFU(因为我编写了它),因此下面是一个将其与原始错误类型一起使用的示例:

// This example uses the simpler syntax supported in Rust 1.34
use snafu::Snafu; // 0.2.0

#[derive(Debug, Snafu)]
enum MyError {
    #[snafu(display("Refrob the Gizmo"))]
    Gizmo,
    #[snafu(display("The widget '{}' could not be found", widget_name))]
    WidgetNotFound { widget_name: String }
}

fn foo() -> Result<(), MyError> {
    WidgetNotFound { widget_name: "Quux" }.fail()
}

fn main() {
    if let Err(e) = foo() {
        println!("{}", e);
        // The widget 'Quux' could not be found
    }
}
//此示例使用Rust 1.34中支持的更简单语法
使用snafu::snafu;//0.2.0
#[派生(调试,Snafu)]
枚举MyError{
#[snafu(显示(“重新查看Gizmo”)]
小发明,
#[snafu(显示(“找不到小部件“{}”,小部件名称))]
WidgetNotFound{widget_name:String}
}
fn foo()->结果{
WidgetNotFound{widget_name:“qux”}.fail()
}
fn main(){
如果让Err(e)=foo(){
println!(“{}”,e);
//找不到小部件“Quux”
}
}
注意,我已经删除了每个枚举值上的冗余
Error
后缀。调用类型
Error
并允许使用者在导入时为类型添加前缀(
mycrate::Error
)或重命名它也是很常见的(
使用mycrate::Error作为FooError


1在实施之前,
说明
是一种必需的方法。

板条箱允许定义自定义错误类型,其样板文件少于上述建议:

custom_error!{MyError
     Io{source: io::Error}             = "input/output error",
     WidgetNotFoundError{name: String} = "could not find widget '{name}'",
     GizmoError                        = "A gizmo error occurred!"
}

免责声明:我是这个板条箱的作者。

我想你已经读过了。:)但是,考虑到存在多个错误的样板箱,答案可能希望扩展到不仅仅是这个条目。感谢您提供另一个详细的、有用的答案——特别是关于从滚动自己的样板箱开始,然后移动到一些板条箱以减少样板箱的建议。非常感谢!
// This example uses the simpler syntax supported in Rust 1.34
use snafu::Snafu; // 0.2.0

#[derive(Debug, Snafu)]
enum MyError {
    #[snafu(display("Refrob the Gizmo"))]
    Gizmo,
    #[snafu(display("The widget '{}' could not be found", widget_name))]
    WidgetNotFound { widget_name: String }
}

fn foo() -> Result<(), MyError> {
    WidgetNotFound { widget_name: "Quux" }.fail()
}

fn main() {
    if let Err(e) = foo() {
        println!("{}", e);
        // The widget 'Quux' could not be found
    }
}
custom_error!{MyError
     Io{source: io::Error}             = "input/output error",
     WidgetNotFoundError{name: String} = "could not find widget '{name}'",
     GizmoError                        = "A gizmo error occurred!"
}