[Rust]thiserror错误处理
thiserror库提供了方便的派生宏,简化了Rust中自定义错误的创建和处理。
编程时,错误处理是无法规避且重要的地方,和Golang一样,Rust没有异常,只有错误。在使用Rust时,编译器会不断督促我们,处理哪些错误。这也使得程序 更加健壮,因为这使得代码运行部署前,就能发现错误并进行适当处理,基本不存在漏掉不处理的情况。
可恢复的(recoverable)和 不可恢复的(unrecoverable)错误
Section titled “可恢复的(recoverable)和 不可恢复的(unrecoverable)错误”Rust 将错误分为两大类:可恢复的(recoverable)和 不可恢复的(unrecoverable)错误。使用Result<T,E>类型,处理可恢复的错误,panic!宏,在程序遇到
不可恢复的错误时直接中断程序的执行。
Rust 使用Result类型类处理可恢复的错误。
enum Result<T, E> Ok(T), Err(E),}Result 是一个枚举类型,T 和 E是泛型类型参数,程序成功运行时返回的值T,
程序运行失败时返回的错误类型E。
Rust在标准库中提供了一个trait,sdt::error::Error,目前错误处理都是基于这个trait来进行,一个结构体/枚举如果实现了这个trait,那么我们认为,它就是一个错误类型。
/// Errors may provide cause information. [`Error::source()`] is generally/// used when errors cross "abstraction boundaries". If one module must report/// an error that is caused by an error from a lower-level module, it can allow/// accessing that error via [`Error::source()`]. This makes it possible for the/// high-level module to provide its own errors while also revealing some of the/// implementation for debugging.#[stable(feature = "rust1", since = "1.0.0")]#[cfg_attr(not(test), rustc_diagnostic_item = "Error")]#[rustc_has_incoherent_inherent_impls]#[allow(multiple_supertrait_upcastable)]pub trait Error: Debug + Display { #[stable(feature = "error_source", since = "1.30.0")] fn source(&self) -> Option<&(dyn Error + 'static)> { None } ...}使用thiserror宏,可以使用极大简化,Cargo.toml中添加:
[dependencies]thiserror = "1.0"编译器支持:要求rustc 1.56+。
thiserror 创建自定义错误
Section titled “thiserror 创建自定义错误”use thiserror::Error;
#[derive(Error, Debug)]pub enum DataStoreError { #[error("data store disconnected")] Disconnect(#[from] io::Error), #[error("the data for key `{0}` is not available")] Redaction(String), #[error("invalid header (expected {expected:?}, found {found:?})")] InvalidHeader { expected: String, found: String, }, #[error("unknown data store error")] Unknown,}Display
Section titled “Display”只要为struct或者每个成员提供#error("..."),那么就会自定义的错误生成Display实现,错误消息支持从错误中插入字段简写:
- #[error(“{var}”)] ⟶ write!(”{}”, self.var)
- #[error(“{0}”)] ⟶ write!(”{}”, self.0)
- #[error(“{var:?}”)] ⟶ write!(”{:?}”, self.var)
- #[error(“{0:?}”)] ⟶ write!(”{:?}”, self.0)
对于每一个含有#[from]属性的,都会生成一个From实现。
#[derive(Error, Debug)]pub enum MyError { Io { #[from] source: io::Error, backtrace: Backtrace, },}错误可以使用 error(transparent) 将 source和 Display 方法直接转发到底层错误,而不添加额外的消息。这适用于需要“任何其他”变体的枚举。
use log::SetLoggerError;
#[derive(Debug, thiserror::Error)]pub enum AppError { #[error("{0} not found")] NotFoundError(String), #[error(transparent)] RedisError(#[from] redis::RedisError),}#[from]属性标记意味着redis::RedisError会被AppError中的RedisError转换为AppError::RedisError。
thiserror vs anyhow
Section titled “thiserror vs anyhow”thiserror 库作者建议到,如果你是在开发库,涉及自己的专用错误类型,便于调用者发生错误时准确收到信息,则用thiserror ,而如果是开发应用,不关心返回的是什么错误类型,只是希望处理起来更简单则使用 anyhow 。