#error #attributes #error-handling #code-generation #proc-macro #error-value

error_generator

使用属性将结构体和枚举转换为完全限定的错误

2个稳定版本

1.2.1 2022年1月13日

#1345过程宏

MIT 许可证

79KB
1.5K SLoC

error_generator

一个用于生成Rust中完全限定错误所需样板代码的crate。

动机

想象一个复杂的方法,有多个可能的失败,比如解析外部资源。你需要

  • 从外部服务器接收资源
  • 将资源解析为领域对象
  • 读取必要的值并检查其有效性

对这个任务的典型方法是为每个操作创建一个错误,如下所示

struct NetworkError { pub response_code: usize }

struct ParseError { pub line: usize }

struct InvalidValueError(pub f32)

为复杂操作创建错误,创建一个枚举,其变体包含单个错误值,如下所示

enum ComplexError {
    Network(NetworkError),
    Parsing(ParseError),
    InvalidValue(InvalidValueError)
}

最终方法看起来像这样

fn complex_function() -> Result<f32, ComplexError> {
    let file = receive_file()?; // maybe a NetworkError
    let the_object = parse_file(file)?; // maybe a ParseError
    let value = check_and_get_value(the_object)?; // maybe an InvalidValueError
    value
}

问题:如果我们想有一个完全限定的错误,我们所有的错误都应该实现std::error::Error trait,这反过来又要求std::fmt::Debug和std::fmt::Display。'Debug'可以推导,但'Error'和'Display'必须手动实现。此外,要使用上面的问号运算符,我们需要为'ComplexError'实现三个额外的std::convert::From实现。

总共需要11个额外的impl-blocks(4 Error,4 Display,3 From)来模拟这个(仍然相当简单)示例。对于这个问题有解决方案,比如'quick_error' crate,但我想要一种更优雅的方式来生成这种样板代码。因为我习惯了像lombok这样的代码生成库,所以我想要创建一些类似的东西,结果就是'error'属性。

用法

关于如何使用此属性的一般说明已记录在属性本身中。现在,我只展示如何在前面示例中使用它。

要像下面那样使用我们的'complex_method',添加'error'属性如下

#[error(message = "The external server returned code {self.response_code} instead of 200.")]
struct NetworkError { pub response_code: usize }

#[error(message = "Syntax error in line {self.line}.")]
struct ParseError { pub line: usize }

#[error(message = "Invalid value, expected '42' but got {self.0}.")]
struct InvalidValueError(pub f32)

#[error(impl_from)]
enum ComplexError {
    #[error(message = "Error while receiving the external file: {_0}")]
    Network(NetworkError),
    #[error(message = "Error while parsing the file: {_0}")]
    Parsing(ParseError),
    #[error(message = "Error while retreiving the value: {_0}")]
    InvalidValue(InvalidValueError)
}

fn complex_function() -> Result<f32, ComplexError> {
    let file = receive_file()?;
    let the_object = parse_file(file)?;
    let value = check_and_get_value(the_object)?;
    value
}

这将使用更少的代码创建任何所需实现(Error,Display,From)。

缺点

  • 缺乏IDE对这些类型宏的支持。IDE会警告你关于即将到来的编译器错误,有关未实现的特征。

未来计划

  • 能够禁用"derive(Debug)"属性,如果您想手动实现Debug

依赖关系

~1.5MB
~36K SLoC