#Conflicting implementations of trait for nested structs

9 messages · Page 1 of 1 (latest)

steep moss
#

I'm trying to use traits to remove arbitrary levels of nesting from nested structs, like peeling an onion:

#[derive(Debug)]
pub struct A();
pub trait Foo {}
impl Foo for A {}

#[derive(Debug)]
pub struct Container<C: Foo> {
    contents: C, //container contains a `Foo`
}
//containers are themselves `Foo` so they can be nested, like layers of an onion
impl<C: Foo> Foo for Container<C> {} 


//T being `ContainsFoo<F>` means T contains a `Foo` of type F and T is itself `Foo`
pub trait ContainsFoo<F: Foo>: Foo {
    fn get(&self) -> &F; //get the contained F
}

//a `Foo` of type F trivially "contains" a `Foo` of type F (itself)
impl<F: Foo> ContainsFoo<F> for F {
    fn get(&self) -> &F {
        self
    }
}

//if C is `ContainsFoo<F>`, then `Container<C>` also contains an F (the one contained in C)
//together with the above trait impl, this should allow us to go from any level of nested containers to any lower level,
//in other words: to peel an arbitrary number of layers from the onion
impl <F: Foo, C: ContainsFoo<F>> ContainsFoo<F> for Container<C> {
    fn get(&self) -> &F {
        self.contents.get()
    }
}

This produces an error:

error[E0119]: conflicting implementations of trait `ContainsFoo<Container<_>>` for type `Container<_>`
  --> src/main.rs:29:1
   |
20 | impl<F: Foo> ContainsFoo<F> for F {
   | --------------------------------- first implementation here
...
29 | impl <F: Foo, C: ContainsFoo<F>> ContainsFoo<F> for Container<C> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Container<_>`

Maybe I've been staring at this for too long, but I can't find a combination of values for the generic parameters that would create such a conflict. Container<_> is not a real type after all?
Playground link: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=2f1c620b2c139f057a73392120b9a525

fading wolf
#

Well the issue is here is simply that you cannot have overlapping impls -- you have impl ContainsFoo for F (for literally any F in existence, all long as F: Foo), and impl ContainsFoo for Container<C>, but those Containers themselves implement Foo, so the first impl already applies. How is the compiler supposed to know which one to call?

#

What you may be looking for is specialization (unstable feature), but that may not fit this usecase, because its not obvious to me which one you want to call in general

steep moss
#

@fading wolf But ContainsFoo is generic: For e.g. Container<A>, the first impl implements ContainsFoo<Container<A>>, while the second one implements ContainsFoo<A>, a different trait!

steep moss
#

I think the issue might be that a hypothetical type B could implement ContainsFoo<Container<B>> which would make the second impl resolve to impl ContainsFoo<Container<B>> for Container<B>, conflicting with the first impl...

#

I think I've convinced myself that that's the problem, but if someone could confirm that that's how it works, that would be great 😄

#

I wonder if something clever can be done with trait bounds to avoid this problem...

rugged kestrel
#

Foo implies ContainsFoo. Why do you need 2 separate traits?

steep moss
#

@rugged kestrel because ContainsFoo<F> is generic and Foo is not. What I'm trying to accomplish is that F: Foo is ContainsFoo<C> for multiple values of C, representing the different layers of nested structure