如何在Rust中定义自定义的“错误”类型? [英] How do you define custom `Error` types in Rust?

查看:744
本文介绍了如何在Rust中定义自定义的“错误”类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个可能返回几种不同错误中的几种的函数。

I'm writing a function that could return several one of several different errors.

fn foo(...) -> Result<..., MyError> {}

我可能需要定义自己的错误类型来表示此类错误。我想这可能是 enum 可能的错误,并且某些 enum 变体附加了诊断数据他们:

I'll probably need to define my own error type to represent such errors. I'm presuming it would be an enum of possible errors, with some of the enum variants having diagnostic data attached to them:

enum MyError {
    GizmoError,
    WidgetNotFoundError(widget_name: String)
}

这是最惯用的方式吗?以及如何实现 Error 特性?

Is that the most idiomatic way to go about it? And how do I implement the Error trait?

推荐答案

您实现< a href = https://doc.rust-lang.org/std/error/trait.Error.html rel = noreferrer> 错误 就像您其他任何特征一样;

You implement Error exactly like you would any other trait; there's nothing extremely special about it:

pub trait Error: Debug + Display {
    fn description(&self) -> &str { /* ... */ }
    fn cause(&self) -> Option<&Error> { /* ... */ }
    fn source(&self) -> Option<&(Error + 'static)> { /* ... */ }
}

说明原因都具有默认实现 1 ,并且您的类型还必须实现 Debug Display ,因为它们是上级。

description, cause, and source all have default implementations1, and your type must also implement Debug and Display, as they are supertraits.

use std::{error::Error, fmt};

#[derive(Debug)]
struct Thing;

impl Error for Thing {}

impl fmt::Display for Thing {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Oh no, something bad went down")
    }
}

当然,事物包含的内容以及方法的实现高度依赖于您的错误类型希望拥有。也许您想在其中包含文件名,或者某种某种整数。也许您想要一个枚举而不是一个 struct 来表示多种类型的错误。

Of course, what Thing contains, and thus the implementations of the methods, is highly dependent on what kind of errors you wish to have. Perhaps you want to include a filename in there, or maybe an integer of some kind. Perhaps you want to have an enum instead of a struct to represent multiple types of errors.

如果最终包装了现有错误,那么我建议实现 From 在这些错误和您的错误之间进行转换。这样一来,您就可以使用 try!并拥有一个符合人体工程学的解决方案。

If you end up wrapping existing errors, then I'd recommend implementing From to convert between those errors and your error. That allows you to use try! and ? and have a pretty ergonomic solution.


这是最惯用的方式吗?

Is that the most idiomatic way to go about it?

我想说的是,一个库将暴露少量(也许是1-3)主要错误类型。这些可能是其他错误类型的枚举。这样一来,您的板条箱使用者就不必处理大量的类型。当然,这取决于您的API以及是否将一些错误集中在一起是否有意义。

Idiomatically, I'd say that a library will have a small (maybe 1-3) number of primary error types that are exposed. These are likely to be enumerations of other error types. This allows consumers of your crate to not deal with an explosion of types. Of course, this depends on your API and whether it makes sense to lump some errors together or not.

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

Another thing to note is that when you choose to embed data in the error, that can have wide-reaching consequences. For example, the standard library doesn't include a filename in file-related errors. Doing so would add overhead to every file error. The caller of the method usually has the relevant context and can decide if that context needs to be added to the error or not.

我建议您手动进行几次,以查看所有部件如何组合在一起。一旦有了这些,您就会对手动完成工作感到厌倦。然后,您可以签出提供宏以减少样板的包装箱:

I'd recommend doing this by hand a few times to see how all the pieces go together. Once you have that, you will grow tired of doing it manually. Then you can check out crates which provide macros to reduce the boilerplate:

  • error-chain
  • failure
  • quick-error
  • Anyhow
  • SNAFU

我的首选库是SNAFU(因为我写了它),因此这是将其与原始错误类型结合使用的示例:

My preferred library is SNAFU (because I wrote it), so here's an example of using that with your original error type:

// 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
    }
}

请注意,我已删除了每个枚举值的冗余 Error 后缀。通常,只调用类型 Error 并允许使用者为类型加上前缀( mycrate :: Error )或在导入时对其重命名(使用mycrate :: Error作为FooError )。

Note I've removed the redundant Error suffix on each enum value. It's also common to just call the type Error and allow the consumer to prefix the type (mycrate::Error) or rename it on import (use mycrate::Error as FooError).

1 RFC 2504 已实施,说明是必需的方法。

1 Before RFC 2504 was implemented, description was a required method.

这篇关于如何在Rust中定义自定义的“错误”类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆