为什么 Iterator<Item = T> 和 Iterator<Item = &T> 的实现有冲突?

Why are implementations of Iterator<Item = T> and Iterator<Item = &T> conflicting?

此代码无法编译:

pub trait ToVec<T> {
    fn to_vec(self) -> Vec<T>;
}

impl<I, T> ToVec<T> for I
where
    I: Iterator<Item = T>,
{
    fn to_vec(self) -> Vec<T> {
        self.collect()
    }
}

impl<'a, I, T> ToVec<T> for I
where
    I: Iterator<Item = &'a T>,
    T: Clone,
{
    fn to_vec(self) -> Vec<T> {
        self.cloned().collect()
    }
}

错误:

error[E0119]: conflicting implementations of trait `ToVec<_>`:
  --> src/lib.rs:14:1
   |
5  | / impl<I, T> ToVec<T> for I
6  | | where
7  | |     I: Iterator<Item = T>,
8  | | {
...  |
11 | |     }
12 | | }
   | |_- first implementation here
13 | 
14 | / impl<'a, I, T> ToVec<T> for I
15 | | where
16 | |     I: Iterator<Item = &'a T>,
17 | |     T: Clone,
...  |
21 | |     }
22 | | }
   | |_^ conflicting implementation

据我了解,当给定类型I实现Iterator时,I::Item只能有一个特定类型,因此它不能满足两种实现。

这是编译器的限制还是我的推理不正确?如果是这样,请提供一个满足这两个impls的例子。

粗略地说,具有 Item = &X 的迭代器将同时满足:

  • 第一个 T == &X => 可能会导致 Vec<&X>
  • 第二个 T == X => 可能导致 Vec<X>

也许专业化(每晚)会有帮助,但我不确定。

泛型类型参数 T 表示的类型集 是泛型类型参数 &T 表示的类型集的超集。它们不是脱节的。 &T 中的所有内容也在 T 中,因此出现冲突的实现消息。

最小示例:

trait Trait {}

impl<T> Trait for T {}

impl<T> Trait for &T {} // compile error

投掷:

error[E0119]: conflicting implementations of trait `Trait` for type `&_`:
 --> src/lib.rs:5:1
  |
3 | impl<T> Trait for T {}
  | ------------------- first implementation here
4 | 
5 | impl<T> Trait for &T {} // compile error
  | ^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&_`

我认为这是问题 #20400,Can't write non-overlapping blanket impls that involve associated type bindings. To summarize, the impls are in fact non-overlapping, but teaching the compiler to recognize that would introduce a form of negative reasoning, which is a major departure from how the trait solver currently works. An RFC 是为了解决这个问题而编写的,但由于对两种类型重叠的含义不明确而被推迟。

看来这个问题最终可能会被重新审视并解决,但这可能需要一些时间。

与此同时,您可以编写一个基于向 Trait 添加类型参数的解决方法,就像我对 Can I avoid eager ambiguity resolution for trait implementations with generics? 的回答一样(尽管在您的情况下,因为您的 impls 实际上从来没有重叠,你永远不必使用涡轮鱼来选择一个 impl;编译器应该总是弄清楚它。)