我正在try 实现一个错误枚举,它可以包含一个与我们的一个特征相关的错误,比如:

trait Storage {
    type Error;
}

enum MyError<S: Storage> {
    StorageProblem(S::Error),
}

我还try 实现From特性,以允许从Storage::Error实例构造MyError:

impl<S: Storage> From<S::Error> for MyError<S> {
    fn from(error: S::Error) -> MyError<S> {
        MyError::StorageProblem(error)
    }
}

(playground)

然而,这未能编译:

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;

我不明白为什么编译器认为这已经实现了.错误消息告诉我已经有了From<MyError<_>>的实现(有),但我不想在这里实现它——我想实现From<S::Error>MyError,从我看到的S::Error的类型不同.

我是不是错过了一些泛型的基础知识?

推荐答案

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

明确地

struct Tricky;

impl Storage for Tricky {
    type Error = MyError<Tricky>;
}

(The set-up here means this doesn't actually compile—MyError<Tricky> is infinitely large—but that error is unrelated to the reasoning about impls/coherence/overlap, and indeed small changes to MyError can make it compile without changing the fundamental problem, e.g. adding a Box like StorageProblem(Box<S::Error>),.)

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

impl From<MyError<Tricky>> for MyError<Tricky> {
    ...
}

implT==MyError<Tricky>的自转换完全匹配,因此编译器不知道 Select 哪一个.Rust编译器不会做出任意/随机的 Select ,而是避免了类似的情况,因此,由于这种风险,必须拒绝原始代码.

这种一致性限制肯定很烦人,这也是specialisation是一个备受期待的特性的原因之一:本质上允许手动指示编译器如何处理重叠...至少,one of the extensions到目前的限制形式允许这样做.

Rust相关问答推荐

如何处理对打包字段的引用是未对齐错误?

泛型属性比较

将已知大小的切片合并成一个数组,

go 掉包装 struct 中的泛型

如果成员都实现特征,是否在多态集合上实现部分重叠的特征?

当一个箱子有自己的依赖关系时,两个人如何克服S每箱1库+n箱的限制?

无符号整数的Rust带符号差

关于 map 闭合求和的问题

为什么`AlternateScreen`在读取输入键时需要按Enter键?

Rust编译器似乎被结果类型与anyhow混淆

为什么 GAT、生命周期和异步的这种组合需要 `T: 'static`?

为什么实现特征的对象期望比具体对象有更长的生命周期?

为什么在 Allocator API 中 allocate() 使用 `[u8]` 而 deallocate 使用 `u8` ?

在 Rust 中,在第一个空格上分割字符串一次

为什么我可以使用 &mut (**ref) 创建两个实时 &mut 到同一个变量?

Rust中如何实现一个与Sized相反的负特性(Unsized)

以 `static` 为前缀的闭包是什么意思?我什么时候使用它?

为什么 Rust 编译器在移动不可变值时执行复制?

通用函数中的生命周期扣除和borrow (通用测试需要)

如何将切片推入数组?