如何在Rust中定义自定义`Error`类型?

时间:2017-03-03 16:49:35

标签: error-handling rust

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

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

我可能需要定义自己的错误类型来表示此类错误。我假设这可能是enum个可能的错误,其中一些enum变体附加了诊断数据:

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

这是最惯用的方式吗?我如何实现Error特征?

3 个答案:

答案 0 :(得分:32)

您实施Error的方式与any other trait完全相同;没有什么特别之处:

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

descriptioncausesource都有默认实现 1 ,您的类型还必须实现Debug和{{1}因为它们是超级特征。

Display

当然,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") } } 包含的内容,以及方法的实现,在很大程度上取决于您希望拥有的错误类型。也许你想在那里包含一个文件名,或者某种整数。也许您希望使用Thing代替enum来表示多种类型的错误。

如果你最终包装现有错误,那么我建议实施struct来转换这些错误和错误。这样您就可以使用Fromtry!并拥有一个非常符合人体工程学的解决方案。

  

这是最习惯的方式吗?

惯用法,我说一个库会有一小部分(可能是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 } } 后缀。只需调用类型Error并允许消费者为类型(Error)添加前缀或在导入时将其重命名(mycrate::Error),这也很常见。

1 RFC 2504实施之前,use mycrate::Error as FooError是必需的方法。

答案 1 :(得分:2)

  

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

这是一种常见的方式,是的。 “惯用语”取决于您希望错误输入的强度,以及您希望如何与其他内容进行互操作。

  

我如何实现错误特征?

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

答案 2 :(得分:1)

板条箱custom_error允许自定义错误类型的定义比上面提出的要少:

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

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