Skip to content

Specialization doesn't work when an impl has an associated type bound #36587

Closed
@kylewlacy

Description

@kylewlacy

(Originally reported in this comment in #31844)

In the following, we have a Child trait to represent a "parent-child" relationship between two types. Alice is a child of () and Bob is a child of Alice. Then, we have a trait Foo that we want to implement for Alice, and for all types that are children of a Foo (so Alice and Bob should both implement Foo):

#![feature(specialization)]

trait Child {
    type Parent;
}

struct Alice;

impl Child for Alice {
    type Parent = ();
}

struct Bob;

impl Child for Bob {
    type Parent = Alice;
}



trait Foo { fn foo(&self); }

impl Foo for Alice {
    fn foo(&self) { println!("Alice foo!"); }
}

// Implement for all children with parents that implement `Foo`
impl<T> Foo for T
    where T: Child, T::Parent: Foo
{
    default fn foo(&self) { println!("Descendant foo!"); }
}



fn main() {
    Alice.foo();
    Bob.foo();
}

(playpen)

Expected output

The program should print "Alice foo!" and "Descendant foo!"

Actual output

The program fails to compile:

error[E0119]: conflicting implementations of trait `Foo` for type `Alice`:
  --> <anon>:28:1
   |
23 | impl Foo for Alice {
   | - first implementation here
...
28 | impl<T> Foo for T
   | ^ conflicting implementation for `Alice`

error: aborting due to previous error

Version info

$ rustc --version
rustc 1.13.0-nightly (55bf6a4f8 2016-09-18)

Other notes

There are two workarounds I've found to fix the error above.

The first is to just remove the T::Parent: Foo clause (which works in this case, but not so much for my actual usecase).

The second workaround is to introduce another trait, FooChild, which is implemented for all types where Self::Parent: Foo. We can then use FooChild in the where clause instead:

trait FooChild { }

impl<T> FooChild for T
    where T: Child, T::Parent: Foo
{
}

// Implement for all children with parents that implement `Foo`
impl<T> Foo for T
//  where T: Child, T::Parent: Foo
    where T: FooChild
{
    default fn foo(&self) { println!("Descendant foo!"); }
}

// ...

(playpen)

Interestingly, the second workaround doesn't work when Child is moved into a separate crate (example repo)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions