如何在MyError类型和anyhow::Error之间进行转换,并处理与std::error::Error的转换冲突?

问题描述 投票:0回答:1

我正在尝试实现

MyError
可以:

  1. anyhow::Error
    ->
    MyError
  2. std::error::Error
    ->
    MyError
  3. MyError
    ->
    anyhow::Error

这是我当前的代码:

#[derive(Debug)]
pub enum MyError {
    Anyhow(anyhow::Error),
    Custom,
}

impl<E> From<E> for MyError
where
    E: std::error::Error + Into<anyhow::Error>,
{
    fn from(e: E) -> Self {
        MyError::Anyhow(e.into())
    }
}

impl From<anyhow::Error> for MyError {
    fn from(e: anyhow::Error) -> Self {
        MyError::Anyhow(e)
    }
}

impl From<MyError> for anyhow::Error {
    fn from(err: MyError) -> Self {
        match err {
            MyError::Anyhow(e) => e,
            MyError::Custom => anyhow::anyhow!("CustomError"),
        }
    }
}

fn main() {
    let io_err = std::fs::read("not_exist_file").unwrap_err();

    // std::io::Error -> MyError
    let my_err_from_std_err: MyError = io_err.into();
    // MyError -> anyhow::Error
    let anyhow_err_from_my_err: anyhow::Error = my_err_from_std_err.into();
    // anyhow::Error -> MyError
    let my_err_from_anyhow: MyError = anyhow_err_from_my_err.into();
}

当我运行此程序时,出现以下错误:

conflicting implementations of trait `From<anyhow::Error>` for type `MyError`
  --> src/main.rs:16:1
   |
7  | / impl<E> From<E> for MyError
8  | | where
9  | |     E: std::error::Error + Into<anyhow::Error>,
   | |_______________________________________________- first implementation here
...
16 |   impl From<anyhow::Error> for MyError {
   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyError`
   |
   = note: upstream crates may add a new impl of trait `std::error::Error` for type `anyhow::Error` in future versions

问题似乎是我的

From<anyhow::Error>
实现与
anyhow::Error
已经实现
std::error::Error
之间存在冲突,从而导致了冲突。

我已经在这个问题上苦苦挣扎了一整天,而且开始感觉这是不可能实现的。

也许如果我可以为

std::error::Error
实现
MyError
,这可以解决冲突吗?如果是这样的话,我愿意接受一个解决方案,其中
MyError
实现
std::error::Error
本身并允许与
anyhow::Error
之间的转换。

rust error-handling type-conversion
1个回答
0
投票

在这种情况下,编译器是你的朋友。答案在错误消息中:

note: upstream crates may add a new impl of trait `std::error::Error` for type `anyhow::Error` in future versions

问题是,你有这两个

impl
块:

impl<E> From<E> for MyError
where
    E: std::error::Error + Into<anyhow::Error>,
{
    fn from(e: E) -> Self {
        MyError::Anyhow(e.into())
    }
}

impl From<anyhow::Error> for MyError {
    fn from(e: anyhow::Error) -> Self {
        MyError::Anyhow(e)
    }
}

现在考虑一下当您尝试将

anyhow::Error
转换为
MyError
时会发生什么。编译器看到第一个块并尝试匹配:
anyhow::Error
实现了
Into<anyhow::Error>
(任何类型
T
都简单地实现了
Into<T>
),但没有实现
std::error::Error
,所以我们在这里应该没问题(剧透)警报:我们不是,但我们稍后会再讨论这一点)。它还看到第二个块,其中有
From<anyhow::Error>
,因此它显然适用并且编译器可以使用它。

但是,您无法保证

anyhow
板条箱维护者将来不会为
std::error::Error
引入
anyhow::Error
的实现。这种更改只需要一个小的 SemVer 修改,因此您的代码可能会突然停止编译,甚至无需更改您的
Cargo.toml
。 Rust 编译器试图阻止这种情况发生,并且不允许你构建你的程序,尽管从技术上来说到目前为止它没有任何问题。

你能用它做什么?嗯,遗憾的是没有那么多。您的选择是:

  1. 删除通用
    From<E>
    实现并为您想要支持的所有错误类型实现
    From
    (可能使用宏)——您的代码不再是通用的
  2. 删除
    std::error::Error
    上绑定的
    From<E>
    并删除
    From<anyhow::Error>
    块 — 需要您删除
    impl From<MyError> for anyhow::Error
    块,因为
    impl<T> From<T> for T
  3. 的总体实现存在冲突
  4. 删除
    Into<anyhow::Error>
    上绑定的
    From<E>
    并在删除
    std::error::Error + Send + Sync + 'static
    时改用
    From<anyhow::Error>
    — 您将无法从
    anyhow::Error
    转换为
    MyError

将来使用专业化这个问题可能会更容易解决,但看起来它不会很快在稳定的 Rust 中可用。

© www.soinside.com 2019 - 2024. All rights reserved.