Trait bounds being dropped?

I'm learning Rust, and trying to implement simple mathematical constructs. For a vector space, one would like to implement Mul<scalar, Output = scalar> and Mul<vector, output = vector> for scalars. With concrete types this is straightforward.

I attempted to write a simple generic implementation, where some trait knows the scalar and vector types and enforces the appropriate trait bounds, and wrote a trivial test function:
(Playground link)

use std::ops::Mul;

pub trait VectorSpace {
    type V;
    type S: Mul<Self::S, Output = Self::S> + Mul<Self::V, Output = Self::V>;
//  type S: Mul<Self::V, Output = Self::V> + Mul<Self::S, Output = Self::S>;
}

fn test<VS: VectorSpace>(v: VS::V, k: VS::S) {
    k * k;
    k * v;
}

gives

error[E0308]: mismatched types
  --> src/lib.rs:12:9
   |
12 |     k * k;
   |         ^ expected VectorSpace::V, found VectorSpace::S

This seems odd, since the trait bound Mul<Self::S, Output = Self::S> is present.

What's stranger is that if the trait bounds are swapped in order (ie. the commented out line), the type mismatch moves.

error[E0308]: mismatched types
  --> src/lib.rs:13:9
   |
13 |     k * v;
   |         ^ expected VectorSpace::S, found VectorSpace::V

From the testing I've been able to do, it seems as if in this case the lexically last Mul<> trait bound is preventing the others from being used. This also happens when the first Mul comes from some supertrait, which seems really weird. I don't know what traits someone else's supertraits might have defined, and being able to break them by specifying additional traits seems very odd.

Any help understanding what's going on would be really welcome.

I wondered whether the compiler doesn't like the two trait bounds potentially overlap, since the compiler can't prove Self::S and Self::V are distinct. In that case I'd have expected to catch an error in the definition of VectorSpace, as you do when you write overlapping trait implementations.

1 Like

Constraint resolution on associated types seems to be quite buggy in general, see for example this issue which has hit many users over time (including me): https://github.com/rust-lang/rust/issues/24159

There's supposed to be a new type / constraint resolution implementation coming, we may have to wait until then.