我试图实现一个错误枚举,它可能包含与我们的某个特征相关的错误,如下所示:
trait Storage {
type Error;
}
enum MyError<S: Storage> {
StorageProblem(S::Error),
}
我还尝试实施From
特征,以允许从MyError
的实例构建Storage::Error
:
impl<S: Storage> From<S::Error> for MyError<S> {
fn from(error: S::Error) -> MyError<S> {
MyError::StorageProblem(error)
}
}
然而,这无法编译:
error[E0119]: conflicting implementations of trait `std::convert::From<MyError<_>>` for type `MyError<_>`:
--> src/main.rs:9:1
|
9 | / impl<S: Storage> From<S::Error> for MyError<S> {
10 | | fn from(error: S::Error) -> MyError<S> {
11 | | MyError::StorageProblem(error)
12 | | }
13 | | }
| |_^
|
= note: conflicting implementation in crate `core`
我不明白为什么编译器认为这已经实现了。错误消息告诉我,已经有From<MyError<_>>
的实现(有),但我没有尝试在此实现 - 我试图实现{{我可以看到1}}和From<S::Error>
与MyError
的类型不同。
我是否遗漏了仿制药的基本内容?
答案 0 :(得分:9)
此处的问题是有人可能会实施Storage
,以便您编写的From
impl与impl<T> From<T> for T
标准库中的impl重叠(也就是说,任何内容都可以转换为本身)。
具体地,
struct Tricky;
impl Storage for Tricky {
type Error = MyError<Tricky>;
}
(这里的设置意味着这并没有实际编译 - MyError<Tricky>
无限大 - 但该错误与关于impl
s / coherence / overlap的推理无关,事实上MyError
的小改动可以在不改变基本问题的情况下进行编译,例如添加Box
StorageProblem(Box<S::Error>),
。
如果我们在你的impl中用Tricky
代替S
,我们会得到:
impl From<MyError<Tricky>> for MyError<Tricky> {
...
}
此impl
与T
== MyError<Tricky>
的自转换完全匹配,因此编译器无法知道选择哪一个。 Rust编译器避免了这样的情况,而不是做出任意/随机的选择,因此必须拒绝原始代码。
这种一致性限制肯定会令人讨厌,这是specialisation备受期待的特性的原因之一:本质上允许手动指示编译器如何处理重叠...至少one of the extensions以当前的限制形式允许。
答案 1 :(得分:1)
一致性问题的解决方法是使用Result::map_err
自行执行转换。然后,您可以使用Result
或try!
:
?
fn example<S: Storage>(s: S) -> Result<i32, MyError<S>> {
s.do_a_thing().map_err(MyError::StorageProblem)?;
Ok(42)
}
当存在具有相同基础Error
的错误变体时,此解决方案也很有用,例如,如果要分离“文件打开”和“文件读取”错误,这两个错误都是{{1} }。