I am learning Rust, have read the Rust homepage, and am trying out small example programs. Here is code that fails:
use std::ops::Add;
pub struct Complex<T> {
pub re: T,
pub im: T,
}
impl <T: Add> Add<Complex<T>> for Complex<T> {
type Output = Complex<T>;
fn add(self, other: Complex<T>) -> Complex<T> {
Complex {re: self.re + other.re, im: self.im + other.im}
}
}
Here is the error message:
src/lib.rs:11:3: 11:59 error: mismatched types:
expected `Complex<T>`,
found `Complex<<T as core::ops::Add>::Output>`
(expected type parameter,
found associated type) [E0308]
src/lib.rs:11 Complex {re: self.re + other.re, im: self.im + other.im}
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I don't understand why it fails to compile.
The Add
trait is defined as
pub trait Add<RHS = Self> {
type Output;
fn add(self, rhs: RHS) -> Self::Output;
}
That is, given a type for Self
(the type the trait is implemented for), and a type for the right-hand-side (RHS
, the thing being added), there will be a unique type that is produced: Output
.
Conceptually, this allows you to create a type A
that can have a type B
added to it, which will always produce a third type C
.
In your example, you have constrained T
to implement Add
. By default, the RHS
type is assumed to be the same as the type the trait is implemented for (RHS = Self
). However, there are no restrictions placed on what the output type must be.
There are two potential solutions:
Say that you will return a Complex
that has been parameterized by whatever the result type of adding T
is:
impl<T> Add<Complex<T>> for Complex<T>
where
T: Add,
{
type Output = Complex<T::Output>;
fn add(self, other: Complex<T>) -> Complex<T::Output> {
Complex {
re: self.re + other.re,
im: self.im + other.im,
}
}
}
Restrict T
to those types that when added to themselves return the same type:
impl<T> Add<Complex<T>> for Complex<T>
where
T: Add<Output = T>,
{
type Output = Complex<T>;
fn add(self, other: Complex<T>) -> Complex<T> {
Complex {
re: self.re + other.re,
im: self.im + other.im,
}
}
}
See also:
Your implementation of add
produces a Complex<<T as core::ops::Add>::Output>
. <T as core::ops::Add>::Output
(i.e. the Output
of the implementation of Add<T>
for T
) is not guaranteed to be the same as T
. You can add a constraint on the Output
associated type to restrict your implementation to be only available when they are in fact the same:
impl<T: Add<Output = T>> Add for Complex<T> {
type Output = Complex<T>;
fn add(self, other: Complex<T>) -> Complex<T> {
Complex { re: self.re + other.re, im: self.im + other.im }
}
}
Or, you could change your implementation to be as generic as possible by making it possible to add a Complex<T>
and a Complex<U>
, provided that it is possible to add a T
and a U
, and by returning a Complex<<T as Add<U>>::Output>
.
impl<T: Add<U>, U> Add<Complex<U>> for Complex<T> {
type Output = Complex<<T as Add<U>>::Output>;
fn add(self, other: Complex<U>) -> Self::Output {
Complex { re: self.re + other.re, im: self.im + other.im }
}
}
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