rust-lang/rust

Disambiguating associated types from super traits in a trait object

vitalyd opened this issue · 7 comments

Suppose you have a trait:

trait Foo: std::ops::Index<usize> + std::ops::Index<isize> {}

How do you declare a Foo trait object, say a Box? The Output associated types need to be specified but they need to be disambiguated between the two super traits. Projections that I’ve tried don’t seem to work but it’s possible I didn’t try the right incarnation.

This stems from the https://users.rust-lang.org/t/how-to-specify-associated-types-with-same-name-in-generics/15677 post.

0b01 commented

The trait system is pretty broken on all fronts. I hope it gets overhauled completely when HKT comes along.

Triage: I'm not aware of a way to do this.

Repro from a duplicate issue:

trait Foo {
    type Error;
}

trait Bar {
    type Error;
}

trait Cake : Foo + Bar {}

struct S {
    f: Box<Cake<Error = i32>>,
}

fn main() {}

There's a similar disambiguation problem here:

trait A {
  type C;
}

trait B: A {
  type C;
}

struct S(Box<dyn B<C=()>>);

though this is less of an issue because it's unlikely B would be defined this way in practice.

It's possible to work around this by defining an intermediate trait (thanks @estebank):

trait Foo {
    type Error;
}

trait Bar {
    type Error;
}

trait Cake : Foo + Bar {}

trait Cake2 : Cake where Self : Foo<Error = i32>, Self : Bar<Error = u64> {}

struct S {
    f: Box<dyn Cake2>,
}

Here's a somewhat complete example of how the above could work (without having to add implementations for Cake2 for every relevant type):

impl<T> Cake2 for T where T: Cake + Foo<Error = i32> + Bar<Error = u64> {}

The error messages about these kind of things are also pretty bad: #100109