How do I get rid of this redundant trait bound at the end of a chain of extended traits?

I've got a bunch of traits extending other traits, adding restrictions to associated types at each step along the chain.

Dummy associated types let me propagate the bounds after introduction, up until the last one, which doesn't seem to stick for some reason. Is there a way to remove the redundant trait bound?

trait HasAValue {
    type X;
    fn x(&self) -> Self::X;
}

trait HasASignedValue: HasAValue {
    type _X: num::Signed;
}

impl<T> HasASignedValue for T
where
    T: HasAValue,
    T::X: num::Signed
{
    type _X = T::X;
}

trait HasAnI32: HasASignedValue<X = i32> {}

trait HasASignedInt: HasASignedValue
where
    Self::X: num::Integer,
{
    type _X: num::Integer;
}

impl<T> HasASignedInt for T
where
    T: HasASignedValue,
    T::X: num::Integer
{
    type _X = T::X;
}

fn _some_function<T>(val: T) -> T 
where 
    T: HasASignedInt,
    T::X: num::Integer // Don't want.  Implied by trait bound
{
    val
}

fn main() {}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 0.39s
     Running `target/debug/playground`

The trick is to put the equality bounds in the supertrait bounds:[1]

trait HasASignedValue: HasAValue<X = <Self as HasASignedValue>::_X> {
trait HasAnI32: HasASignedValue<_X = i32> {}
trait HasASignedInt: HasASignedValue<_X = <Self as HasASignedInt>::_X> {

If you give the associated types distinct names, there's less ambiguity and you can just use Self::_:

trait HasASignedValue: HasAValue<X = Self::SignedX> {
trait HasAnI32: HasASignedValue<SignedX = i32> {}
trait HasASignedInt: HasASignedValue<SignedX = Self::SignedIntX> {

The things I changed were:

  • Added equality bound to HasASignedValue
  • Changed HasAnI32 equality to be on the supertrait associated type, not the super-supertrait
  • Added equality bound to HasASignedInt and removed a redundant bound:
     trait HasASignedInt: HasASignedValue
    -where
    -  Self::X: num::Integer,
     {
         type _X: num::Integer;
     }
    
    (The removed portion does the same thing as the bound on the associated type.)

  1. the bounds attached directly to the trait, or bounds attached directly to Self -- they do the same thing ↩ī¸Ž

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.