#error #thiserror #error-handling #primary #variant #aims #glue

typederror

anyhow 的包装器,允许指定主要错误类型

4 个版本

0.2.2 2024 年 5 月 18 日
0.2.1 2024 年 5 月 18 日
0.2.0 2024 年 5 月 11 日
0.1.0 2024 年 5 月 11 日

#706Rust 模式

每月 32 次下载

MIT/Apache

25KB
317

typederror

anyhow 的包装器,但具有“主要”错误类型。

动机

此库旨在成为 anyhowthiserror 之间的粘合剂。它允许你为调用者应匹配的变体定义主要错误类型,同时仍然捕获途中可能发生的任何其他错误。

记录函数的错误类型

如果你简单地返回一个 anyhow::Error,调用者不知道期望哪种错误。他们需要阅读你的代码来确定可能出现的错误类型。

通过使用 TError,你可以指定调用者应匹配的主要错误类型。这有助于记录函数的主要错误类型。

fn my_fallible_function() -> typederror::Result<(), MyError> {
    // Do something that might fail.
    let s = std::fs::read_to_string("file.txt").map_err(|e| MyError::IoError(e))?;
    // NOTE: if `MyError` implements `From<std::io::Error>`,
    // you can do `std::fs::read_to_string("file.txt").terror()?` instead.
    some_operation(s)?; // An error we don't need to match on.
    Ok(())
}

主要错误类型可以是派生自 thiserror::Error 的枚举,其中只有有意义的错误被枚举捕获,而其他错误则被底层的 anyhow::Error 捕获。

你还可以实现 DefaultError,以便将所有其他错误捕获在主要错误类型的特殊“通配符”变体中。

#[derive(Debug, thiserror::Error)]
enum MyError {
   #[error("IO error: {0}")]
   IoError(#[from] std::io::Error),
   #[error("{0}")]
   Misc(typederror::anyhow::Error)
}

impl DefaultError for MyError {
    fn from_anyhow(err: typederror::anyhow::Error) -> Self {
        Self::Misc(err)
    }
}

向下转换为主要错误类型

由于 TError 已经知道主要错误类型,它可以提供向下转换为该类型的便利方法。这使得你可以更轻松地处理单一种类的错误,而无需在多个不同的错误类型上进行匹配。

if let Err(err) = my_fallible_function() { // returns Result<T, TError<MyError>>
    match err.get() {
        MyError::IoError(e) => { // e is of type `std::io::Error`
            // Handle the error.
        }
        MyError::Misc(e) => { // e is of type `anyhow::Error`
            // Handle the error.
        }
    }
}

如果需要,你还可以向下转换为其他类型,就像使用 anyhow 一样。

match err.downcast_ref::<serde::Error>() {
    Ok(e) => {
        // Handle serde error.
    }
    Err(e) => {
        // Handle other error.
    }
}

从简单开始,然后添加错误变体

为了帮助你开始,你可以使用 TError<()> 作为主要错误类型。或者使用 typederror::Result<T> 作为函数的返回类型。这将有效地与 anyhow 一样工作,让你可以在编写代码时不必担心错误类型。

fn do_something() -> typederror::Result<()> {
    // Do something.
    my_fallible_function()?;
    Ok(())
}

稍后,当您想为函数创建特定的变体以便调用者更容易匹配时,您可以创建一个枚举,派生自thiserror::Error,并使用它作为主要错误类型。您需要添加任何必要的转换,但只需添加您想要匹配的变体。

所有其他错误将按照anyhow的行为捕获,或者它们可以通过在枚举上实现DefaultError特质来捕获到枚举的特殊“通配符”变体。

注意事项

遗憾的是,?运算符不能自动将错误类型转换为您的首选错误类型。

例如

#[derive(Debug, thiserror::Error)]
enum MyError {
    #[error("IO error: {0}")]
    IoError(#[from] std::io::Error),
    #[error("{0}")]
    Misc(anyhow::Error)
}

impl DefaultError for MyError {
    fn from_anyhow(err: anyhow::Error) -> Self {
        Self::Misc(err)
    }
}

fn my_fallible_function() -> typederror::Result<(), MyError> {
   let s = std::fs::read_to_string("file.txt")?;
   // Do something else with s.
   Ok(())
}

fn main() {
    if let Err(e) = my_fallible_function() {
        match e.get() {
            // ...
        }
    }
}

在上面的示例中,?运算符不会自动将std::io::Error转换为MyError::IoError,就像您直接使用MyError作为错误类型时那样。错误将匹配为MyError::Misc,在调用e.get()时。

为了正确捕获IoError,将函数的第一行改为

let s = std::fs::read_to_string("file.txt").terror()?;

最低支持的 Rust 版本

thirtyfour的最低支持 Rust 版本目前是1.75,并将根据依赖项的需要进行更新。

许可证

根据您的选择,在Apache许可证版本2.0MIT许可证下授权。
除非您明确声明,否则任何贡献,根据Apache-2.0许可证定义,提交给此crate的,都应如上所述双许可,没有任何附加条款或条件。
`SPDX-License-Identifier: MIT OR Apache-2.0`

依赖项

~130KB