使用泛型类型时,如何实现"From"的冲突?

Fra*_*ser 9 rust

我正在尝试实现一个错误枚举,它可能包含与我们的某个特征相关的错误,如下所示:

trait Storage {
    type Error;
}

enum MyError<S: Storage> {
    StorageProblem(S::Error),
}
Run Code Online (Sandbox Code Playgroud)

我还试图实现From特性以允许MyError从一个实例构建Storage::Error:

impl<S: Storage> From<S::Error> for MyError<S> {
    fn from(error: S::Error) -> MyError<S> {
        MyError::StorageProblem(error)
    }
}
Run Code Online (Sandbox Code Playgroud)

(游乐场)

但是这无法编译:

error[E0119]: conflicting implementations of trait `std::convert::From<MyError<_>>` for type `MyError<_>`:
 --> src/lib.rs:9:1
  |
9 | impl<S: Storage> From<S::Error> for MyError<S> {
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: conflicting implementation in crate `core`:
          - impl<T> std::convert::From<T> for T;
Run Code Online (Sandbox Code Playgroud)

我不明白为什么编译器认为这已经实现了.错误消息告诉我已经有一个From<MyError<_>>(有)的实现,但我不是试图在这里实现 - 我正在尝试实现,From<S::Error>并且MyError与S::Error我能看到的类型不同.

我是否遗漏了仿制药的基本内容?

huo*_*uon 10

这里的问题是有人可能会实现,Storage以便From你写的impl与标准库中的impl重叠impl<T> From<T> for T(也就是说,任何东西都可以转换为自身).

特别,

struct Tricky;

impl Storage for Tricky {
    type Error = MyError<Tricky>;
}
Run Code Online (Sandbox Code Playgroud)

(这里的设置意味着这实际上不是编译 - MyError<Tricky>无限大 - 但该错误与关于impls/coherence /重叠的推理无关,实际上小的改变MyError可以使其编译而不改变基本问题,例如添加一个Box像StorageProblem(Box<S::Error>),.)

如果我们用你的impl Tricky代替S,我们得到:

impl From<MyError<Tricky>> for MyError<Tricky> {
    ...
}
Run Code Online (Sandbox Code Playgroud)

这impl与使用T== 的自转换完全匹配MyError<Tricky>,因此编译器不知道选择哪一个.Rust编译器避免了这样的情况,而不是进行任意/随机选择,因此必须拒绝原始代码,因为存在这种风险.

这种一致性限制肯定会令人讨厌,并且是专业化是一个备受期待的特性的原因之一:本质上允许手动指示编译器如何处理重叠...至少,当前受限形式的扩展之一允许这样做.

  • 明确的答案:谢谢。我想如果错误消息说“可能存在”冲突的实现而不是暗示已经存在,那么它可能会更清晰一些。 (2认同)

She*_*ter 5

解决一致性问题的一种方法是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)
}
Run Code Online (Sandbox Code Playgroud)

当存在具有相同基础的错误变体时Error,例如,如果您要分隔“文件打开”和“文件读取”错误,两者都为,则该解决方案也很有价值io::Error。