I'm trying to write some generic math functions in Rust and I keep running into the following error message:
error: conflicting implementations for trait SoAndSo
Is it possible to solve the problem? If so, how?
For example, I'm trying to write a generic dot product that takes two iterators, zips them and iterates over the pairs to accumulate the products. I want this function also to be able to compute complex-valued dot products. The dot product over complex numbers involves conjugating one side. My first idea was to write a trait Dot1
for a binary function to replace Mul
in that it also conjugates the left hand side argument. Here's the complete code:
extern crate num;
use num::complex::Complex;
use num::{Float, Num};
trait Dot1<Rhs, Result> {
fn dot1(&self, rhs: Rhs) -> Result;
}
impl<T: Float> Dot1<T, T> for T {
// conjugation for reals is a no-op
fn dot1(&self, rhs: T) -> T {
*self * rhs
}
}
impl<T: Num + Clone> Dot1<Complex<T>, Complex<T>> for Complex<T> {
fn dot1(&self, rhs: Complex<T>) -> Complex<T> {
self.conj() * rhs
}
}
fn main() {
println!("Hello, world!")
}
Since a Complex<T>
is not Float
, there should be no overlap between the two "generic impls". I didn't expect any problems, but every time I try to provide more than one "generic impl" for a trait, the compiler doesn't like it:
error[E0119]: conflicting implementations of trait `Dot1<num::Complex<_>, num::Complex<_>>` for type `num::Complex<_>`:
--> src/main.rs:17:1
|
10 | impl<T: Float> Dot1<T, T> for T {
| ------------------------------- first implementation here
...
17 | impl<T: Num + Clone> Dot1<Complex<T>, Complex<T>> for Complex<T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `num::Complex<_>`
|
= note: upstream crates may add new impl of trait `num::Float` for type `num::Complex<_>` in future versions
How can I write a generic, iterator-based dot product that works for both real and complex numbers? Taking the iterators, zipping them, etc. is not a problem and I was even able to figure out what type parameters to use with which bounds. I don't seem able to "unify" certain data types using traits such as the one from above.
The problem here is the (current) compiler does not register that Complex<T>
does not implement Float
. Imagine if Complex
did implement Float
: either there would have to be some way to decide which impl
to use, or the overlapping code would have to be outlawed... but what if someone only adds the impl<T> Float for Complex<T>
in some other crate the compiler doesn't know about?
This last point is key: unlike Haskell, Rust's design allows this code to be perfectly OK without any risk, all because Rust differs in how it handles the last point. If you are in a crate that can see a type Type
and a trait Trait
, then you know for 100% sure if Type
implements Trait
: Rust doesn't have Haskell's open world assumption, as you can only write impl Trait for Type
if either Trait
or Type
lie in the current crate (compilation unit), that is, you can't have orphan instances, where someone implements Float
for Complex<T>
in some distant crate.
RFC 24 (that Chris links to) recognises this, allowing these generic implementations to coexist with more specific ones, as long as the implementations are guaranteed to not overlap.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With