我正在编写一个可以返回几个不同错误中的几个错误的函数。
fn foo(...) -> Result<..., MyError> {}
我可能需要定义自己的错误类型来表示此类错误。我假设这可能是enum
个可能的错误,其中一些enum
变体附加了诊断数据:
enum MyError {
GizmoError,
WidgetNotFoundError(widget_name: String)
}
这是最惯用的方式吗?我如何实现Error
特征?
答案 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)> { /* ... */ }
}
description
,cause
和source
都有默认实现 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
来转换这些错误和错误。这样您就可以使用From
和try!
并拥有一个非常符合人体工程学的解决方案。
这是最习惯的方式吗?
惯用法,我说一个库会有一小部分(可能是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!"
}
免责声明:我是这个板条箱的作者。