Overly strict coherence for constrained blanket implementations?

Just making sure I understand coherence correctly. This restriction conceptually doesn't make sense, right?

struct LocalType;

impl<I> LocalTrait for I where I: IntoIterator<Item = LocalType> {}

trait LocalTrait {}

impl LocalTrait for &[u8] {}

yields

error[E0119]: conflicting implementations of trait `LocalTrait` for type `&[u8]`
 --> src/lib.rs:7:1
  |
3 | impl<I> LocalTrait for I where I: IntoIterator<Item = LocalType> {}
  | ------------------------ first implementation here
...
7 | impl LocalTrait for &[u8] {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&[u8]`
  |
  = note: upstream crates may add a new impl of trait `std::iter::Iterator` for type `&[u8]` in future versions

(Playground.)

No other crates would be able to implement LocalTrait for &[u8], right? Is there already an issue about this?

But std may implement IntoIterator (or Iterator) for &[u8].

But not with Item = LocalType.


On first glance, this does indeed look like rustc is simply not smart enough here. This isnā€™t helped by the fact that thereā€™s a general T: IntoIterator impl for T: Iterator. Maybe the compiler would be less confused without such a blanket impl around. Nevermind, thatā€™s easily ruled out, too.


This situation may be related how impls like this wonā€™t compile either due to ā€œoverlapā€

struct LocalType1;
struct LocalType2;

trait LocalTrait {}

impl<I> LocalTrait for I where I: Iterator<Item = LocalType1> {}
impl<I> LocalTrait for I where I: Iterator<Item = LocalType2> {}
1 Like

Coherence does not take associated types into account in any situation to my knowledge. I have no doubt it would be accepted if someone cared enough to implement it.

An RFC to take associated types into account was postponed in 2016, so there are at least some concerns:

4 Likes

I have even worse case of unnecessary strictness of the current constraints:

pub trait LocalTrait { .. }

impl<T: LocalTrait> ThridPartyTrait for T { .. }

Another crate will not be able to implement both LocalTrait and ThridPartyTrait for its local type, because the blanket impl would cause a compilation error. There are ways for working around it (blanket impl for dyn LocalTrait or introduction of wrappers), but they are quite unpleasant.

This is something that sealed traits could potentially impact, if LocalTrait is sealed. (Exactly what the rules would be I don't know, but there's an opportunity if we know that all the possible implementations for a trait are in the current crate.)

2 Likes

In all cases when I encountered this restriction a sealed LocalTrait would have been useless.