我正在编写一个函数,该函数可能返回几种不同错误中的几种。
fn foo(...) -> Result<..., MyError> {}
我可能需要定义自己的错误类型来表示此类错误。我认为这可能是一个enum
错误,其中某些enum
变体附带了诊断数据:
enum MyError {
GizmoError,
WidgetNotFoundError(widget_name: String)
}
这是最惯用的方式吗?以及如何实现该Error
特征?
你实施的方式Error
和其他任何特质一样 ; 没有什么特别的:
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
和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
而不是a 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
)或在导入(use mycrate::Error as FooError
)上重命名也是很常见的。
1在实施RFC 2504之前,这description
是必需的方法。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句