Why do trait bounds not differentiate between Assotiated types?

When I tried to implement a Trait for Iterators with different Items i got an error.
Is there a way to get something like this to work?
It would be fine if the solution requires nightly feature flags, since I am using some others anyway.

I already tried the specialization feature flag, but that didn't help.

trait Test {
    fn test(&self);
}

impl<T: Iterator<Item = i32>> Test for T {
    fn test(&self) {
        println!("{}", std::any::type_name::<Self>())
    }
}
impl<T: Iterator<Item = char>> Test for T {
    fn test(&self) {
        println!("{}", std::any::type_name::<Self>())
    }
}

fn main() {
    vec![1,2,3,4].into_iter().test();
    vec!['A', 'B', 'C'].into_iter().test();
}

failed to compile with error:

error[E0119]: conflicting implementations of trait `Test`
  --> src/main.rs:11:1
   |
6  | impl<T: Iterator<Item = i32>> Test for T {
   | ---------------------------------------- first implementation here
...
11 | impl<T: Iterator<Item = char>> Test for T {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation

Playground

3 Likes

No, that's not possible.

2 Likes

I think you can work around this by introducing some (possibly sealed, if you want) helper trait that i32 and char implement, and then writing a single impl of Test for T where T::Item: HelperTrait.

2 Likes

Back on my computer now, let me illustrate this with a code example :slight_smile:

trait Test {
    fn test(&self);
}

trait TestForIteratorOfItem {
    fn test<This: Iterator<Item = Self>>(this: &This);
}
impl<T: Iterator> Test for T
where
    T::Item: TestForIteratorOfItem,
{
    fn test(&self) {
        T::Item::test(self)
    }
}

impl TestForIteratorOfItem for i32 {
    fn test<This: Iterator<Item = i32>>(this: &This) {
        println!("{}", std::any::type_name::<This>())
    }
}
impl TestForIteratorOfItem for char {
    fn test<This: Iterator<Item = char>>(this: &This) {
        println!("{}", std::any::type_name::<This>())
    }
}

fn main() {
    vec![1, 2, 3, 4].into_iter().test();
    vec!['A', 'B', 'C'].into_iter().test();
}

Rust Playground

This solution seems really nice, but it isn't really applicable to my actual issue which is more like this:
I want to do two different implementations depending if T is a trait object or not.

To do that I wanted to use the ptr_metadatafeature flag and the Ponintee trait to differentiate between them.

So i would like to do one implementation for Pointee<Metadata = DynMetadata<Dyn>> and one for Pointee<Metadata = ()>.

However I realized that I can just use two traits, although that is not ideal for usability.

Also is there any reason why this shouldn't work (and would adding this need a RFC)?

Example of this:

trait FancyStuffDynamic {
    fn is_trait_object(&self) -> bool;
}
trait FancyStuffStatic {
    fn is_trait_object(&self) -> bool;
}


impl<Dyn: ?Sized, T: ?Sized + Pointee<Metadata = DynMetadata<Dyn>>> FancyStuffDynamic for T {
    fn is_trait_object(&self) -> bool {
        println!("Type: {}, t_obj: {}", std::any::type_name::<T>(), std::any::type_name::<Dyn>());
        true
    }
}
impl<T: Pointee<Metadata = ()>> FancyStuffStatic for T {
    fn is_trait_object(&self) -> bool {
        println!("Type: {}, {} ", std::any::type_name::<T>(), std::any::type_name::<()>());
        false
    }
}

Rust Playground

Reading through the rest of your answer, I don’t quite see why the same kind of approach wouldn’t be applicable.


Edit: For example here’s a straightforward approach with a helper trait: Rust Playground

Ok i guess you are right.
I'm still not 100% happy with having to use this trait and forward all the calls, but I think its better than what i have currently so I'll mark it as a solution.

However I still don't understand why rust doesn't allow the direct way.

Here is a declined postponed RFC proposing that the coherence check allow such impls; you can read the discussion there for some background on why the feature was not accepted, links to further reading, etc.

2 Likes

Thank you, i'll give it a read!

(Postponed and not declined per se.)

1 Like

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.