Skip to main content

2 posts tagged with "thiserror"

View All Tags

在Rust的错误处理生态系统中,从标准库的std::error::Erroranyhowthiserrorsnafu, 每个库都在用法和功能上进行了不同程度的改进和演变。 下面是对这些库的改进和设计的详细说明。

标准库的std::error::Error

特点

  • 基础特性:定义了一个通用的错误trait,所有错误类型都可以实现这个trait。
  • 手动实现:需要手动实现DisplayError trait,比较繁琐。

用法示例

use std::fmt;

#[derive(Debug)]
struct MyError {
details: String,
}

impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.details)
}
}

impl std::error::Error for MyError {}

小结

要使用Rust标准库的Error trait实现自定义错误,那么需要做到以下两点:

  • 实现std::fmt::Display trait,以便将错误信息显示给用户。
  • 实现std::error::Error trait,以便将错误信息传递给调用者。

anyhow

该库提供了 anyhow::Error,一种基于特征对象(trait object)的错误类型, 用于在 Rust 应用程序中轻松地进行惯用错误处理。

改进

  • 简化错误处理:提供了一个基于trait对象的通用错误类型anyhow::Error,简化了错误的传播和处理。
  • 上下文信息:可以为错误添加上下文信息,帮助调试。
  • 自动回溯:自动捕获和打印回溯信息(在Rust 1.65及以上)。

用法示例

use anyhow::{Context, Result};

fn read_file(path: &str) -> Result<String> {
let content = std::fs::read_to_string(path)
.with_context(|| format!("Failed to read file at path: {}", path))?;
Ok(content)
}

小结

anyhow库通过提供anyhow::Error类型,简化了Rust中的错误处理。 它提供了一种简单的方式来处理和传播错误,同时支持添加上下文信息和自动回溯信息。

  • 在anyhow中,经常使用的就是anyhow::Resultanyhow::anyhow
  • 可以使用?符号进行错误传播,同时可以解包未出错的值

thiserror

这个库为标准库的 std::error::Error trait 提供了一个方便的派生宏。

改进

  • 派生宏:通过派生宏简化了自定义错误类型的定义。
  • 自动实现:自动实现DisplayError trait,减少了手动编码的负担。
  • 与其他错误类型集成:可以轻松地将其他错误类型转换为自定义错误类型。

用法示例

use thiserror::Error;

#[derive(Error, Debug)]
pub enum MyError {
#[error("IO error")]
Io(#[from] std::io::Error),
#[error("Parse error")]
Parse(#[from] std::num::ParseIntError),
}

snafu

SNAFU 是一个可以轻松生成错误并向底层错误添加信息的库, 特别是当相同的底层错误类型可能发生在不同的上下文中时。

改进

  • 错误类型生成:通过宏生成错误类型和相关的上下文信息。
  • 上下文支持:更强大的上下文信息支持,提供了详细的错误信息。
  • 源错误追踪:内置对源错误的追踪和显示。

用法示例

use snafu::{Snafu, ResultExt};

#[derive(Debug, Snafu)]
enum MyError {
#[snafu(display("Failed to open file {}: {}", filename, source))]
OpenFile { filename: String, source: std::io::Error },
#[snafu(display("Failed to parse integer: {}", source))]
ParseInt { source: std::num::ParseIntError },
}

fn read_file(path: &str) -> Result<String, MyError> {
let content = std::fs::read_to_string(path).context(OpenFile { filename: path.to_string() })?;
Ok(content)
}

总结

从Rust标准库的std::error::Erroranyhowthiserrorsnafu,经历了一系列的改进和设计:

  • 从手动到自动:从标准库需要手动实现错误处理,到thiserrorsnafu利用宏自动生成代码,减少了开发者的负担。
  • 上下文信息anyhowsnafu增强了对上下文信息的支持,使调试更加容易。
  • 错误传播简化anyhow通过统一的错误类型简化了错误传播,而snafu提供了强大的错误上下文支持。
  • 回溯信息anyhow自动捕获回溯信息,为调试提供了更多有用的信息。

这些改进和设计使得Rust的错误处理更加简洁、高效和易于维护。

参考

鱼雪

thiserror 库为 Rust 的 std::error::Error trait 提供了一个方便的派生宏,使得定义错误类型变得更加简单和高效。下面是对 thiserror 中不同属性的用法和含义的总结,以及它们的使用情况。

Thiserror 思维导图

#[error("...")]

  • 用法: 用于为错误类型或枚举的每个变体提供一个显示格式(Display)。
  • 含义: 定义了当错误被打印时显示的消息格式。
  • 适用情况: 任何需要向用户展示错误信息的场景。

#[from]

  • 用法: 用于自动实现从源错误类型到当前错误类型的 From trait。
  • 含义: 允许一个错误类型自动转换("from")另一个错误类型。
  • 适用情况: 当你想要从一个特定的错误类型自动转换到你定义的错误类型时。这通常用在错误链的上下文中,允许底层错误被包装成更高层的抽象错误。

#[source]

  • 用法: 标记一个字段作为源错误(即导致当前错误的底层错误)。
  • 含义: 该字段会被 Error trait 的 source() 方法返回,用于错误链的追踪。
  • 适用情况: 当你的错误是由另一个错误导致的,并且你想要保留这种因果关系时。这对于调试和错误报告非常有用。

#[backtrace]

  • 用法: 标记一个字段为回溯(backtrace)信息。
  • 含义: 允许捕获和存储错误发生时的调用栈信息。
  • 适用情况: 在需要调试或详细了解错误发生上下文时非常有用。这通常用于复杂系统中,通过回溯可以更容易地定位问题源头。

#[error(transparent)]

  • 用法: 使得当前错误类型在显示和源链处理上透明地代理到内部的错误类型。
  • 含义: 当前错误类型的 Displaysource 方法将直接委托给它包装的错误类型。
  • 适用情况: 当你定义的错误类型仅仅是对另一个错误类型的简单封装,而你不希望添加任何额外信息或行为时。这在定义通用或透明的错误包装时特别有用。

通过这些属性,thiserror 库大大简化了 Rust 中错误处理的复杂性,使得定义丰富而又具有表达力的错误类型变得非常简单。 无论是简单的直接转换,还是更复杂的错误链处理和调试信息捕获,thiserror 都提供了强大而灵活的工具,以支持各种不同的使用场景。 相关信息可以在其官方文档GitHub 仓库中找到。

鱼雪