我正在尝试实现
MyError
可以:
anyhow::Error
-> MyError
。std::error::Error
-> MyError
。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
之间的转换。
在这种情况下,编译器是你的朋友。答案在错误消息中:
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 编译器试图阻止这种情况发生,并且不允许你构建你的程序,尽管从技术上来说到目前为止它没有任何问题。
你能用它做什么?嗯,遗憾的是没有那么多。您的选择是:
From<E>
实现并为您想要支持的所有错误类型实现 From
(可能使用宏)——您的代码不再是通用的std::error::Error
上绑定的 From<E>
并删除 From<anyhow::Error>
块 — 需要您删除 impl From<MyError> for anyhow::Error
块,因为 impl<T> From<T> for T
Into<anyhow::Error>
上绑定的 From<E>
并在删除 std::error::Error + Send + Sync + 'static
时改用 From<anyhow::Error>
— 您将无法从 anyhow::Error
转换为 MyError
。将来使用专业化这个问题可能会更容易解决,但看起来它不会很快在稳定的 Rust 中可用。