0

This code fails to compile:

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:

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

From what I understand, when a given type I implements Iterator, I::Item can only have one specific type, so it cannot satisfy both implementations.

Is this a limitation of the compiler or is my reasoning incorrect? If so, please provide an example which satisfies both impls.

pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
apilat
  • 1,370
  • 8
  • 17
  • The same issue is mentioned in [How to allow multiple implementations of a trait on various types of IntoIterator items?](https://stackoverflow.com/q/34470995/3650362) but the workaround there doesn't seem to apply to a case like yours. – trent Feb 17 '21 at 19:09

3 Answers3

3

I believe this is issue #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 was written to fix the issue, but postponed partly due to ambiguity about what it means for two types to overlap.

It seems likely this issue will be revisited and fixed eventually, but it may take some time.

In the mean time, you might write a workaround based on adding a type parameter to Trait, as in my answer to Can I avoid eager ambiguity resolution for trait implementations with generics? (Although in your case, since your impls never actually overlap, you never have to use the turbofish to pick an impl; the compiler should always just figure it out.)

trent
  • 25,033
  • 7
  • 51
  • 90
  • In issue #20400 the `impl`s are clearly not overlapping because the example given uses different concrete types for each impl, e.g. `u8` and `u16`. However, in OP's case, since he's using `T` and `&'a T` where the `T` is a distinct generic type parameter in both `impl`s, we can pick `&'a i32` for the first `T` and `i32` for the second `T` and then we'll have `&'a i32 == &'a i32` which is an overlap. Even if issue #20400 is fixed I do not believe OP's example will compile even then. – pretzelhammer Feb 17 '21 at 19:46
  • 1
    @pretzelhammer If you pick `T = &'a i32` in the first `impl` and `T = i32` in the second, the two `impl`s implement `ToVec<&'a i32>` and `ToVec` respectively, so they are not overlapping because they do not implement the same trait. There is no way to choose all four type parameters such that both `impl`s implement the same trait for the same type. – trent Feb 17 '21 at 20:26
  • Oh I see! Thanks for explaining! – pretzelhammer Feb 17 '21 at 20:45
  • 1
    Thanks for the detailed explanation. [This](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0b6884afd121def9fbfc3ac86df9a7b2) is the trick from the linked answer applied in this case. – apilat Feb 17 '21 at 21:18
2

Roughly speaking, an iterator with Item = &X would satisfy both:

  • The first one with T == &X => could result in a Vec<&X>
  • The second one with T == X => could result in a Vec<X>

Maybe specialization (nightly) could help, but I am not sure.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
phimuemue
  • 34,669
  • 9
  • 84
  • 115
  • I was under the impression that this is allowed. For example, this [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1034ee67a236bc4231cc812d449d38d7) code compiles. – apilat Feb 17 '21 at 12:23
  • 3
    That shouldn't be a conflict because `ToVec<&T>` and `ToVec` are different traits, too. Definitely possible I'm missing something though. – trent Feb 17 '21 at 12:23
  • 1
    @apilat they are different traits, but also `T` being a superset over `&T` doesn't mean `i32` is a superset over `&i32` – kmdreko Feb 17 '21 at 12:27
2

The set of types represented by the generic type parameter T is a superset of the set of types represented by the generic type parameter &T. They are not disjoint. Everything in &T is also in T hence the conflicting implementations message.

Minimal example:

trait Trait {}

impl<T> Trait for T {}

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

Throws:

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 `&_`
pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
  • The difference in your example is that Trait does not have a generic type parameter. If I add one, the code [compiles](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=941a5ab248273f838d7237c7f2ed1cdb). – apilat Feb 17 '21 at 12:34
  • @apilat [no it does not](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=233b39e856fc51054b94518b8074d611). – pretzelhammer Feb 17 '21 at 12:36
  • In my original example, both implementations are for `ToVec`, **not** `ToVec<&T>`, so I don't see how your playground example applies. – apilat Feb 17 '21 at 12:38
  • @apilat all the examples I have shared with you so far have been to illustrate a simple fundamental concept of how generics work in Rust: which is that `T` is a superset of `&T`. This is true regardless of how contrived you make the example. It's always true. It doesn't matter if you give the traits generic type parameters or associated types. [Here's a more contrived example similar to the one in your question](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0c42d1d31b22026a51983c2c7f36b666). – pretzelhammer Feb 17 '21 at 13:01
  • @apilat I've even written [a blog post about this](https://github.com/pretzelhammer/rust-blog/blob/master/posts/common-rust-lifetime-misconceptions.md#1-t-only-contains-owned-types). Please let me know if it helps you! – pretzelhammer Feb 17 '21 at 13:03
  • 1
    Thanks, that helps explain why `T` is a superset of `&T` but this isn't what I'm trying to argue against. Consider [this example](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9901d80156194a710a9c1d89e779394b) using `P` and `Q` for clarity. In my understanding, the only way we could have overlapping implementations is if `P == Q`. But then `O: Other + Other` which is impossible since `P != &P`, so we can't actually have overlapping implementations by contradiction. Is my reasoning correct here? – apilat Feb 17 '21 at 13:15
  • 1
    You have overlapping implementations as soon as `P == &Q` – Jmb Feb 17 '21 at 13:25
  • @Jmb In that case, I have `impl Trait

    for O` and `impl Trait<&P> for O` which do not overlap ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1034ee67a236bc4231cc812d449d38d7) with manual implementation).

    – apilat Feb 17 '21 at 13:33
  • @apilat `T` being a superset of `&T` does not mean `i32` is a superset of `&i32`. The rule does not apply (or make any sense) when talking about concrete types. A single concrete type is a single concrete type, it's not a set of types. The principle is only true in the case of generic types. That's why you can have `i32` and `&i32` implementations without conflicts, whereas `T` and `&T` implementations do conflict. – pretzelhammer Feb 17 '21 at 13:38
  • @pretzelhammer Which rule are you referring to? When `P == &Q` (even in the generic case), [the implementations don't conflict](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1034ee67a236bc4231cc812d449d38d7). – apilat Feb 17 '21 at 14:00
  • @apilat "rule" may not be the best word for it, but I'm referring to the generic type superset concept. Also, that playground example you linked has no generic types in it, only concrete types, so I don't understand what you are talking about. There's no `P` or `&Q` anywhere in that example. – pretzelhammer Feb 17 '21 at 14:02
  • @pretzelhammer Sorry, [this](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4e667bac6ffe082f41485cdf9166a398) is the correct link. I believe this is what the previous example reduces to when `P == &Q`. – apilat Feb 17 '21 at 14:06
  • 1
    No, when `P == &Q`, you get `impl Trait<&Q> for &Q {}` which [fails too](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=769a92647485b72277cff0930993d995). – Jmb Feb 17 '21 at 14:33
  • @Jmb The type on the RHS of `for` is `OtherTrait` which is the same as `OtherTrait`, but in your playground the types on the RHS are different. Perhaps the more accurate example is [this](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e7faadeb37f437e652d10047c9b73d22), but it is still allowed. – apilat Feb 17 '21 at 15:36
  • @Jmb Maybe this is where my misunderstanding comes from, but (coming back to [the original example](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9901d80156194a710a9c1d89e779394b)) I think that in order to have an implementation conflict you need both the implemented trait and the type it is implemented on be the same in both implementations. Since the trait must be the same: `Trait

    == Trait` so `P == Q`. However, since the type must also be the same, `Other == Other` so `P == &Q`. But these can't happen at the same time, so there is no conflict

    – apilat Feb 17 '21 at 15:44