Part 1. Drawing attention to the problem
Hello, I'm not a very experienced rustacean and initially I've stuck trying to write code like:
trait Container: IntoIterator { /* snip */ }
impl<T: Container> IntoIterator for T { /* snip */ }
But this is the wrong code because IntoIterator
already has a blanket impl.
This is quite disappointing because in other languages I could implement the interface on the base class, but in Rust I must implement it manually on each type again and again - goodbye DRY (macros are not a graceful approach).
And moreover what if setting IntoIterator
as supertrait of Container
is an afterthought? We cannot add IntoIterator
as supertrait because it would be a breaking change for our API because we cannot add corresponding blanket impl.
I discovered some proposals like specialization (seems like does not resolve this problem at all) and negative bounds (seems like it is a too big change and has some problems).
Then I've tried to find some other way to solve the problem and some thoughts are below. Maybe it has obvious disadvantages - at least I tried.
Part 2. Some thoughts
So. In the initial example above the compiler is worried about the overlap. But why every type must struggle and don't use ready impl because some other potential type can trigger overlap? What about just apply no one of conflicting impls in the case of the overlap?
In most cases a concrete type e.g. Container1
will not implement Iterator
, so here the ready impl for IntoIterator
will be used.
But let's imagine some strange type ContainerAndIterator
that implements both Container
and Iterator
, in this case the compiler will just say that we must manually implement IntoIterator
because it is supertrait.
What about the afterthought-case mentioned above? Let's say we have:
trait Container { /* snip */ }
And we want to add supertrait IntoIterator
without breaking anything, we could write:
trait Container: IntoIterator { /* snip */ }
impl<T: Container> IntoIterator for T { /* snip */ }
impl<T: Container + Iterator> IntoIterator for T { /* snip */ }
And if the 2 impls are the same we could use the syntax sugar like:
trait Container: IntoIterator { /* snip */ }
impl<T: Container + ?Iterator> IntoIterator for T { /* snip */ }
Update: It seems like the above violates the orphan rules. Maybe we could weaken them for this special case e.g. because Container
is local.