Rust has support for generics but sometimes, can be really hard to get your head around. That is because of its strong, edge cutting type system.

That good news is that Traits are here to help, in particular, when it comes to “shape” a generic type via trait bounds.

Implementing complex numbers helped me get a grasp on how to use generic types and traits in Rust.

If u are not comfortable with complex numbers, take a quick look to this quick introduction

## Generics ¶

So what do I mean by “Implementing Complex Numbers in Rust”?

I need a data structure which has multiplication (cross product), dot product with another complex number, addition, comparison, has its complex conjugate and has a standard way to be displayed.

A complex number hold two numbers, so let’s start with:

```
struct Complex {
Real: f64,
Imaginary: f64,
}
```

Here is already a great opportunity to add generics. (Real, Imaginary) can be any type number: `f32`

, `f64`

or even `i128`

(quite dangerous).

The great news here is that those types are Copy types and `Complex<T>`

, being composed of Copy types, can also a Copy type. That means overall less hassle with the Borrow Checker.

The crate `num_traits`

has a handy trait `Num`

that unifies all types of numbers.

```
use num_traits::Num as Number;
#[derive(PartialEq, PartialOrd, Eq, Copy, Clone, Debug)]
struct Complex<T: Number> {
Real: T,
Imaginaire: T,
}
impl<T: Copy + Number> Complex<T> {
pub fn new(imaginary: T, real: T) -> Complex<T> {
Complex {
Real: real,
Imaginaire: imaginary,
}
}
}
```

## Overloading operations ¶

Rust supports operator overloading which is nice for better reading.

Instead of having to invoke `add`

and `mul`

functions for all complex, using overloaded operations for more concise (less verbose in Rust loll)

```
Complex<T> * Complex<T> => Complex<T> // (cross product not dot product)
Complex<T> + Complex<T> => Complex<T>
```

Overloading is done by implementing a trait: the trait `Add<Complex<T>>`

.

For `+`

, `Complex<T>`

has to implement the `Add<Complex<T>>`

trait with

```
impl<T: Copy + Number + Neg<Output = T>> Add<Complex<T>> for Complex<T> {
type Output = Complex<T>;
fn add(self, another: Complex<T>) -> Complex<T> {
Complex {
Imaginaire: another.Imaginaire + self.Imaginaire,
Real: another.Real + self.Real,
}
}
}
```

And for `*`

, `Complex<T>`

has to implement the `Mul<Complex<T>>`

trait with

```
impl<T: Copy + Number + Neg<Output = T>> Mul<Complex<T>> for Complex<T> {
type Output = Complex<T>;
fn mul(self, another: Complex<T>) -> Complex<T> {
let real = (self.Real * another.Real) - (self.Imaginaire * another.Imaginaire);
let im = (self.Real * another.Imaginaire) + (self.Imaginaire * another.Real);
Complex {
Real: real,
Imaginaire: im,
}
}
}
```

Dot Product:

```
pub fn dot_product(&self, another: Complex<T>) -> T {
(self.Real * another.Imaginaire) - (self.Imaginaire * another.Real)
}
```

for conjugate:

```
pub fn conjugate(&self) -> Complex<T> {
Complex {
Imaginaire: -self.Imaginaire,
Real: self.Real,
}
}
```

## Traits for the win ¶

While implementing the `Display`

Trait for `Complex<T>`

, I fell on the need of zero.
I needed to know if the sign of the Imaginary part of my current number or if it was nil.

By chance, numbers in Rust implement the `Zero`

Trait. YOUPPPIII
That Trait gives access to the zero of a generic type.

```
impl<T: Copy + Number + Zero + PartialOrd + Display> Display for Complex<T> {
fn fmt(&self, f: &mut Formatter) -> Result {
match self {
&Complex {
Real: real,
Imaginaire: im,
} if real.is_zero() && !im.is_zero() => write!(f, "{}i", im),
&Complex {
Real: real,
Imaginaire: im,
} if !real.is_zero() && im.is_zero() => write!(f, "{}", real),
&Complex {
Real: real,
Imaginaire: im,
} if im < T::zero() => write!(f, "{}{}i", real, im),
&Complex {
Real: real,
Imaginaire: im,
} => write!(f, "{}+{}i", real, im),
}
}
}
impl<T: Clone + Copy + Neg<Output = T> + Number + Zero> Zero for Complex<T> {
fn zero() -> Complex<T> {
Complex::new(T::zero(), T::zero())
}
fn is_zero(&self) -> bool {
self.Real.is_zero() && self.Imaginaire.is_zero()
}
}
```

## Example ¶

```
let i = complex::Complex::<f64>::new(3.0, 3.0);
let j = complex::Complex::<f64>::new(3.0, 3.0);
println!("ADD: {}", j + i); // ADD: 6+6i
println!("MULT: {}", j * i); // MULT: 0+18i
println!("DOT Product: ({}).({}) = {}", i, j, i.dot_product(j)); // DOT Product: (3+3i).(3+3i) = 0
println!("Conjugate: {}", i.conjugate()); // Conjugate: 3-3i
```

All the code is available, here. However this library is way more complete, even if it share the same thought process.