thiserror

355 阅读3分钟

thiserror 学习

thiserror 是 Rust 中一个用于简化错误定义的库,它提供了一种方便的方式来定义自定义错误类型,同时支持自动实现 std::error::Error trait。通过 thiserror,可以轻松地为你的错误类型添加 DisplayFrom 实现,使得错误处理更加简洁和结构化。

1. 添加依赖

首先,在 Cargo.toml 中添加 thiserror 依赖:

[dependencies]
thiserror = "1.0"

2. 使用 thiserror 定义自定义错误类型

thiserror 通过派生(derive)的方式,自动为错误类型实现 std::error::ErrorDisplay trait。以下是一个简单的示例:

示例:基本用法

use thiserror::Error;

#[derive(Debug, Error)]
pub enum MyError {
    #[error("An error occurred: {0}")]
    SimpleError(String),

    #[error("File not found: {0}")]
    FileNotFound(String),

    #[error("Network error: {source}")]
    NetworkError {
        #[from]
        source: std::io::Error,
    },

    #[error("Invalid input: {0}")]
    InvalidInput(String),
}

fn main() -> Result<(), MyError> {
    // 返回一个简单的自定义错误
    Err(MyError::SimpleError("Something went wrong".to_string()))?;
    
    Ok(())
}

解释:

  1. #[derive(Debug, Error)]: 自动为 MyError 类型实现了 std::error::Errorstd::fmt::Display
  2. #[error("An error occurred: {0}")]: 为不同的枚举变体定义错误消息格式,支持占位符 {} 的格式化输出。

3. 自动 from 转换

thiserror 允许通过 #[from] 特性,自动将其他错误类型转换为自定义的错误类型。这在需要将不同的错误类型整合为一个统一的错误类型时非常有用。

示例:#[from] 特性

use thiserror::Error;
use std::fs::File;
use std::io::{self, Read};

#[derive(Debug, Error)]
pub enum MyError {
    #[error("IO error occurred: {source}")]
    IoError {
        #[from]
        source: io::Error,
    },

    #[error("Custom error: {0}")]
    CustomError(String),
}

fn read_file(path: &str) -> Result<String, MyError> {
    let mut file = File::open(path)?; // 自动将 io::Error 转换为 MyError
    let mut content = String::new();
    file.read_to_string(&mut content)?;
    Ok(content)
}

fn main() -> Result<(), MyError> {
    let _ = read_file("non_existing_file.txt")?;
    Ok(())
}

解释:

  • File::open(path)file.read_to_string(&mut content) 返回 io::Error 时,由于 #[from] 特性,它们会被自动转换为 MyError::IoError
  • #[from] 可以减少手动编写 From<OtherError> 实现的代码。

4. 复杂错误类型

thiserror 还支持在自定义错误类型中传递额外的信息,例如将其他错误作为 source,方便使用 source() 方法链式访问底层错误。

示例:嵌套的 source 错误

use thiserror::Error;

#[derive(Debug, Error)]
pub enum ConfigError {
    #[error("Configuration file not found: {0}")]
    FileNotFound(String),

    #[error("Configuration parse error: {source}")]
    ParseError {
        #[from]
        source: toml::de::Error,
    },
}

fn parse_config(file_content: &str) -> Result<ConfigError, ConfigError> {
    // 这里模拟 TOML 解析错误
    let _config: toml::Value = toml::from_str(file_content)?; // 自动转换解析错误
    Ok(ConfigError::FileNotFound("Missing config".to_string()))
}

fn main() -> Result<(), ConfigError> {
    let file_content = "invalid_toml_syntax";
    parse_config(file_content)?;

    Ok(())
}

解释:

  • ParseError 使用 #[from],自动将 toml::de::Error 转换为 ConfigError::ParseError,并将其作为 source 错误传递。
  • 这样,当遇到嵌套错误时,用户可以通过 .source() 获取底层错误。

5. 显示和调试

自定义错误可以自动支持 DisplayDebug,并且通过 thiserror 定义的错误可以很方便地进行格式化输出。例如:

fn main() {
    let err = MyError::FileNotFound("config.toml".to_string());
    println!("Error: {}", err); // 使用 Display
    println!("Debug: {:?}", err); // 使用 Debug
}

总结

  • thiserror 提供了一种简洁的方式定义自定义错误类型,自动实现了 ErrorDisplay,简化了错误处理代码。
  • 通过 #[from],可以轻松地将其他错误类型转换为自定义的错误类型,减少手动编写代码的复杂度。
  • thiserror 适合用于需要精细控制错误类型的场景,尤其是在构建库或复杂应用程序时。