【问题标题】:Why are implementations of Iterator<Item = T> and Iterator<Item = &T> conflicting?为什么 Iterator<Item = T> 和 Iterator<Item = &T> 的实现会发生冲突?
【发布时间】:2021-02-17 12:10:16
【问题描述】:

这段代码编译失败:

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 的示例。

【问题讨论】:

标签: generics rust polymorphism traits parametric-polymorphism


【解决方案1】:

我相信这是问题 #20400,Can't write non-overlapping blanket impls that involve associated type bindings。总而言之,impls 实际上是不重叠的,但教编译器认识到这会引入一种否定推理形式,这与 trait solver 目前的工作方式有很大不同。 An RFC 是为了解决这个问题而编写的,但部分原因是由于两种类型重叠的含义不明确而被推迟。

这个问题似乎最终会被重新审视和修复,但可能需要一些时间。

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

【讨论】:

  • 在 issue #20400 中,impls 显然没有重叠,因为给出的示例对每个 impl 使用不同的具体类型,例如u8u16。但是,在 OP 的情况下,由于他使用 T&amp;'a T,其中 Timpls 中不同的泛型类型参数,我们可以选择 &amp;'a i32 作为第一个 Ti32对于第二个T,然后我们将有&amp;'a i32 == &amp;'a i32,这是一个重叠。即使问题 #20400 得到修复,我也不相信 OP 的示例即使在那时也能编译。
  • @pretzelhammer 如果你在第一个impl和第二个T = i32中选择T = &amp;'a i32,那么这两个impls分别实现ToVec&lt;&amp;'a i32&gt;ToVec&lt;i32&gt;,所以它们不是重叠,因为它们没有实现相同的特征。没有办法选择所有四个类型参数,以便 impls 为同一类型实现相同的特征。
  • 哦,我明白了!感谢您的解释!
  • 感谢您的详细解释。 This 是在这种情况下应用的链接答案的技巧。
【解决方案2】:

泛型类型参数T 表示的类型集是泛型类型参数&amp;T 表示的类型集的超集。它们并不脱节。 &amp;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 `&_`

【讨论】:

  • 您的示例中的不同之处在于 Trait 没有泛型类型参数。如果我添加一个,代码compiles.
  • @apilat no it does not.
  • 谢谢,这有助于解释为什么T&amp;T 的超集,但这不是我要反对的。为了清楚起见,考虑使用PQ 使用this example。据我了解,我们可以有重叠实现的唯一方法是P == Q。但是然后O: Other&lt;Type=P&gt; + Other&lt;Type=&amp;P&gt; 这是不可能的,因为P != &amp;P,所以我们实际上不能通过矛盾来重叠实现。我的推理在这里正确吗?
  • 你有重叠的实现只要P == &amp;Q
  • 不,当P == &amp;Q,你得到impl&lt;Q&gt; Trait&lt;&amp;Q&gt; for &amp;Q {} 哪个fails too
【解决方案3】:

粗略地说,带有Item = &amp;X 的迭代器将满足两者:

  • 第一个带有T == &amp;X => 的结果可能是Vec&lt;&amp;X&gt;
  • 第二个带有T == X => 的结果可能是Vec&lt;X&gt;

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

【讨论】:

  • 我的印象是这是允许的。例如,这个playground 代码编译。
  • 这不应该是冲突,因为ToVec&lt;&amp;T&gt;ToVec&lt;T&gt; 也是不同的特征。绝对有可能我错过了一些东西。
  • @apilat 它们是不同的特征,但 T&amp;T 的超集并不意味着 i32&amp;i32 的超集
猜你喜欢
  • 2010-10-14
  • 1970-01-01
  • 2019-10-30
  • 2022-11-21
  • 1970-01-01
  • 2023-01-14
  • 2011-06-27
  • 2012-06-16
  • 2013-02-18
相关资源
最近更新 更多