Requiring generic type T allow f32 * &T, while doing f32 * f32 in code

Hi all,
I am writing some linear algebra toy code and encountered the following compile error.
At a high level, I want to implement a routine that multiplies f32 to a generic vector type V.
However, the trait bound I added for f32 * &V operation seems to let the compiler "forget" that f32 * f32 is legal and leads to the following compilation error for the function not_working().

I did manage to find a walk around by putting the floating point multiplication in a seperate function. But I really want to understand why this happens.

PS:
I know that in this specific use case, I could instead do
for<'a> &'a V: core::ops::Mul<f32, Output=V>
and use
v * (a * b)
instead of
a * b * v
But since commutativity is not implied by Mul (correct me if I got this wrong), this alternate approach may not always work.

fn not_working<V>(a: f32, b: f32, v: &V) -> V where
    f32: for<'a> core::ops::Mul<&'a V, Output=V>,
{
    a * b * v
}

#[inline(always)]
fn mul_f32(x: f32, y: f32) -> f32 { x * y }

fn working<V>(a: f32, b: f32, v: &V) -> V where
    f32: for<'a> core::ops::Mul<&'a V, Output=V>,
{
    mul_f32(a, b) * v
}

fn main() {}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/main.rs:13:9
   |
13 |     a * b * v
   |         ^ expected `&V`, found `f32`
   |
   = note: expected reference `&V`
                   found type `f32`

error[E0369]: cannot multiply `V` by `&V`
  --> src/main.rs:13:11
   |
13 |     a * b * v
   |     ----- ^ - &V
   |     |
   |     V
   |
help: consider further restricting type parameter `V`
   |
11 |     f32: for<'a> core::ops::Mul<&'a V, Output=V>, V: std::ops::Mul<Output = &V>
   |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Some errors have detailed explanations: E0308, E0369.
For more information about an error, try `rustc --explain E0308`.
error: could not compile `playground` due to 2 previous errors

There's an issue for this shortcoming/bug which I could not immediately find; basically, the compiler limits its consideration of trait implementors to those explicitly listed in the where clause, even if it wouldn't be so limited elsewhere.

Thus:

 fn not_working<V>(a: f32, b: f32, v: &V) -> V where
     f32: for<'a> core::ops::Mul<&'a V, Output=V>,
+    f32: core::ops::Mul<f32, Output=f32>,
 {
     a * b * v
 }
3 Likes

This issue, to be precise.

2 Likes

Thanks! This helps a lot. BTW I just found out that swaping the two lines of where clause in the code you've given will break the compilation, like this.

Ouch. Probably issue 41756.

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.