The following MWE trying to define all possible multiplications of a container type (containing an abstract multiplicable type) and its references with a float causes the rust compiler to choke up with an evaluation overflow. Based on the message, somehow in the second impl it cannot decide what impl to call if the fourth one is defined. Any idea how to fix it? How to constrain a generic impl to not apply to reference types?
use std::ops::*;
struct A<T> {
v : T
}
impl<T> Mul<f64> for A<T> where T : Mul<f64> {
type Output = A<<T as Mul<f64>>::Output>;
fn mul(self, w : f64) -> Self::Output { A{ v : self.v * w} }
}
impl<T> Mul<A<T>> for f64 where f64 : Mul<T> {
type Output = A<<f64 as Mul<T>>::Output>;
fn mul(self, x : A<T>) -> Self::Output { A{ v : self * x.v} }
}
impl<'a, T> Mul<f64> for &'a A<T> where &'a T : Mul<f64> {
type Output = A<<&'a T as Mul<f64>>::Output>;
fn mul(self, w : f64) -> Self::Output { A{ v : &(self.v) * w} }
}
// If you remove this
impl<'b, T> Mul<&'b A<T>> for f64 where f64 : Mul<&'b T> {
type Output = A<<f64 as Mul<&'b T>>::Output>;
fn mul(self, x : &'b A<T>) -> Self::Output { A { v : self * &(x.v) } }
}
fn main() {
let t = A{v : 1.0};
let b = 3.0*&t; // ... and this, then it compiles.
let c = &t*3.0;
}
If I add type annotations in the impls, the problem just moves to the application in main. The code can be made to compile if an explicit version of mul is always selected, but that is not practical in daily use:
use std::ops::*;
struct A<T> {
v : T
}
impl<T> Mul<f64> for A<T> where T : Mul<f64> {
type Output = A<<T as Mul<f64>>::Output>;
fn mul(self, w : f64) -> Self::Output { A{ v : <T as Mul<f64>>::mul(self.v, w) } }
}
impl<T> Mul<A<T>> for f64 where f64 : Mul<T> {
type Output = A<<f64 as Mul<T>>::Output>;
fn mul(self, x : A<T>) -> Self::Output { A{ v : <f64 as Mul<T>>::mul(self, x.v) } }
}
impl<'a, T> Mul<f64> for &'a A<T> where &'a T : Mul<f64> {
type Output = A<<&'a T as Mul<f64>>::Output>;
fn mul(self, w : f64) -> Self::Output { A{ v : <&'a T as Mul<f64>>::mul(&(self.v), w)} }
}
impl<'b, T> Mul<&'b A<T>> for f64 where f64 : Mul<&'b T> {
type Output = A<<f64 as Mul<&'b T>>::Output>;
fn mul(self, x : &'b A<T>) -> Self::Output { A { v : <f64 as Mul<&'b T>>::mul(self, &(x.v)) } }
}
fn main() {
let t : A<f64> = A{v : 1.0};
let _a = <f64 as Mul<&A<f64>>>::mul(3.0, &t); // This explicit typing works
let _b = 3.0*(&t); // If you remove this, then it compiles.
let _c = &t*3.0;
}
The error in this version (for the _b-line at the end) is
error[E0275]: overflow evaluating the requirement `f64: std::ops::Mul<&A<_>>`
So it seems as if the compiler was looking for arbitrary Mul<&A<_> instead of Mul<&A<f64>>, which it very well should know from that it needs based on the type annotations of t and even without the explicit annotations.