Compiler asks me to implement a trait, but then calls it a conflicting impl?

Hi

I'm running into conflicting implementations of trait GetInner<Table<_>> in the below code (playground link).

If I delete what the compiler identifies as a conflicting implementation, the compiler then tells me that I have an unexpected argument in let bridge = parent.connect_to(&child); (because it can only connect_to Table<Parent>) and that I should implement the (conflicting) trait as I had it before.

I am out of my depth here. Any ideas on how to resolve this? Thanks!

use std::marker::PhantomData;

trait Named {}

trait Unit {
    type Name: Named;
}

struct Table<N: Named> {
    _phantom: PhantomData<N>
}

impl<N: Named> Unit for Table<N> {
    type Name = N;
}

struct Bridge<L: Unit, R: Unit> {
    name: (L::Name, R::Name)
}

trait GetInner<O: Unit> {
    type Inner;
    
    fn inner(&self, other: & O) -> Self::Inner;
}

trait ConnectTo<R: Unit>: Sized + Unit
    where Bridge<Self, R>: GetInner<Self> + GetInner<R>
{
    fn connect_to(&self, other: &R) -> Bridge<Self, R>;
}

impl<L: Named, R: Named> ConnectTo<Table<R>> for Table<L> {
    fn connect_to(&self, _other: &Table<R>) -> Bridge<Self, Table<R>> {
        unimplemented!()
    }
}

impl<L: Named, R: Named> GetInner<Table<R>> for Bridge<Table<L>, Table<R>> {
    fn inner(&self, _other: & Table<R>) -> Self::Inner {
        unimplemented!()
    }
}

impl<L: Named, R: Named> GetInner<Table<L>> for Bridge<Table<L>, Table<R>> {
    fn inner(&self, _other: & Table<L>) -> Self::Inner {
        unimplemented!()
    }
}

struct Parent;
struct Child;

impl Named for Parent {}
impl Named for Child {}

struct Foo {
    parent: Table<Parent>,
    child: Table<Child>,
    bridge: Bridge<Table<Parent>, Table<Child>> 
}

impl Foo {
    fn new(parent: Table<Parent>, child: Table<Child>) -> Self {
        let bridge = parent.connect_to(&child);
        
        Self {
            parent,
            child,
            bridge
        }
    }
}

fn main() {

}
1 Like

The problem is that both implementations would apply to

//           v         v  same type
Bridge<Table<T>, Table<T>>
//     ^^^^^^^^  ^^^^^^^^ same type

Without specialization,[1] I think you'd have to fall back to keeping either the GetInner<Table<L>> or GetInner<Table<R>> version and use concrete types for the rest.

For example.[2]


  1. not coming any time soon ↩︎

  2. n.b. I removed or moved some other bounds in passing and am too lazy to undo that; in general you don't want trait bounds on structs or non-supertrait bounds on traits if you can avoid it ↩︎

Actually if you remove the bound on the ConnectTo trait altogether, the reliance on GetInner disappears. Probably you would need to adjust the bounds on your actual implementation of ConnectTo, but this may be a way forward.

With that change, the rest of this comment isn't necessary for the OP, but I'll keep it in case it's useful.


You could use newtypes to make your blanket implementations of GetInner non-overlapping.

struct RightOf<B>(B);
impl<L: Named, R: Named> GetInner<Table<R>> for RightOf<&Bridge<Table<L>, Table<R>>> {
    type Inner = ();
    fn inner(&self, _other: &Table<R>) -> Self::Inner {
        unimplemented!()
    }
}

struct LeftOf<B>(B);
impl<L: Named, R: Named> GetInner<Table<L>> for LeftOf<&Bridge<Table<L>, Table<R>>> {
    type Inner = ();
    fn inner(&self, _other: &Table<L>) -> Self::Inner {
        unimplemented!()
    }
}

Then you would use it something like

LeftOf(&bridge).get_inner(&left_table);

Alternatively you could have GetLeft and GetRight traits. Or you could generalize that with another parameter to GetInner which indicates left or right (in the case of a Bridge).


Alternative to all of these things, you could take a step back and try to come up with a more targeted design, instead of trying to be maximally generic.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.