How to compare to 0 for generic type parameters

I am trying to implement my own version of Complex (purely for my own knowledge). I am trying to have the division operation implemented. But I am having trouble comparing the denominator with 0 value of corresponding type. How do I do that? Basically if the division is by 0 I want to return my own error.

I mean, the zero value of i_ is 0 (where _ is 8, 32, 64 etc) and the zero value of f_ is 0.0 (_ is 32 or 64). Also my below implementation does not stop me from using &str as a type. How can I restrict the Complex to be only of numeric type?

#[derive(Debug)]
pub struct Complex<T> {
    re: T,
    im: T,
}

impl<T> Complex<T>
where
    T: Sized + Copy,
{
    pub fn new(re: T, im: T) -> Self {
        Self { re, im }
    }
}

impl<T> Div for Complex<T>
where
    T: Sized + Copy + Div<Output = T>,
{
    type Output = Result<Complex<T>, String>;
    fn div(self, rhs: Complex<T>) -> Self::Output {
        Ok(Self {
            re: self.re / rhs.re,
            im: self.im / rhs.im,
        })
    }
}

Unfortunately, checked_div isn’t packaged into a trait in std, but the num_traits crate provides one.

So, you could write something like this:

fn my_div<T:Copy+num_traits::CheckedDiv>(a:T,b:T) -> Result<T,&'static str> {
    a.checked_div(&b).ok_or("divide by zero")
}

What traits does a number have? If you need to be able to compare to 0, then the type would have to be able to convert From<u8> and be comparable, at least being able to check for equality (PartialEq, Eq). There are also the traits Add, Sub, Mul and Div. As long as a type implements all those traits, it doesn't matter, if it's a number or something else, does it? For convenience, you want to create a trait, that covers all these properties and auto-implement the trait for all types that meet these requirements.

trait Number: From<u8> + PartialEq + Add + Sub + Mul + Div {}

impl<T> Number for T where T: From<u8> + PartialEq + Add + Sub + Mul + Div {}

Then you can simply write T: Number as a type requirement for your complex number.

Thank you for your comments. Now I am stuck with f32 not supporting CheckedDiv

use num::Num;
use std::ops::Div;

#[derive(Debug)]
pub struct Complex<T> {
    re: T,
    im: T,
}

impl<T> Complex<T>
where
    T: Sized + Copy + Num,
{
    pub fn new(re: T, im: T) -> Self {
        Self { re, im }
    }
}

impl<T> Div for Complex<T>
where
    T: Sized + Copy + Div<Output = T> + num::CheckedDiv,
{
    type Output = Result<Complex<T>, &'static str>;
    fn div(self, rhs: Complex<T>) -> Self::Output {
        let re = (self.re).checked_div(&rhs.re);
        let im = (self.im).checked_div(&rhs.im);
        match (re, im) {
            (Some(r), Some(i)) => Ok(Self { re: r, im: i }),
            (_, _) => Err("Divide by zero"),
        }
    }
}

Thank you. This is a wonderful way of looking at the problem. I will try to implement it and see how it goes.

Please wrap your code between ``` so it gets formatted correctly.

Just checking

use num::Num;
use std::ops::Div;

#[derive(Debug)]
pub struct Complex<T> {
    re: T,
    im: T,
}

impl<T> Complex<T>
where
    T: Sized + Copy + Num,
{
    pub fn new(re: T, im: T) -> Self {
        Self { re, im }
    }
}

impl<T> Div for Complex<T>
where
    T: Sized + Copy + Div<Output = T> + num::CheckedDiv,
{
    type Output = Result<Complex<T>, &'static str>;
    fn div(self, rhs: Complex<T>) -> Self::Output {
        let re = (self.re).checked_div(&rhs.re);
        let im = (self.im).checked_div(&rhs.im);
        match (re, im) {
            (Some(r), Some(i)) => Ok(Self { re: r, im: i }),
            (_, _) => Err("Divide by zero"),
        }
    }
}

Thank you for the suggestion. I will keep a note of it next time onwards.

Unfortunately, division of complex numbers doesn't work this way — it's more complex than dividing real and imaginary parts separately. Moreover, the notation x / y usually returns the number directly (as opposed to use a Result), and it's recommended to keep custom implementations of Div consistent with that.

Here's the implementation for num::Complex, for reference:

// (a + i b) / (c + i d) == [(a + i b) * (c - i d)] / (c*c + d*d)
//   == [(a*c + b*d) / (c*c + d*d)] + i [(b*c - a*d) / (c*c + d*d)]
impl<T: Clone + Num> Div<Complex<T>> for Complex<T> {
    type Output = Self;

    #[inline]
    fn div(self, other: Self) -> Self::Output {
        let norm_sqr = other.norm_sqr();
        let re = self.re.clone() * other.re.clone() + self.im.clone() * other.im.clone();
        let im = self.im * other.re - self.re * other.im;
        Self::Output::new(re / norm_sqr.clone(), im / norm_sqr)
    }
}

Note that it handles division by zero the way floating point numbers do (inf and nan), which is different from the semantics of checked_div.

1 Like

got it. I used Zero trait as well.

use num::Num;
use std::ops::Div;
use num::Zero;

#[derive(Debug)]
pub struct Complex<T> {
    re: T,
    im: T,
}

impl<T> Complex<T>
where
    T: Sized + Copy + Num,
{
    pub fn new(re: T, im: T) -> Self {
        Self { re, im }
    }
}

impl<T> Div for Complex<T>
where
    T: Sized + Copy + Div<Output = T> + Zero,
{
    type Output = Result<Complex<T>, &'static str>;
    fn div(self, rhs: Complex<T>) -> Self::Output {
        if num::Zero::is_zero(&rhs.re) || num::Zero::is_zero(&rhs.im){
            return Err("Divide by zero")
        }
        Ok(Self{re: self.re / rhs.re, im: self.im / rhs.im})
    }
}

Thanks.

By the way, how do I know which trait will be useful? Is there any list of available traits?

You are right. But I wanted to start simple. Basically the complicated formula comes down to same a/b, c/d.
where b = d = norm_sqrt() = 0. only possible if b and d are 0. (hopefully).

By the way, you can use the edit button under each of your first two posts in this thread to add the two lines of backticks that will cause those already-written posts to display their Rust code formatted properly. I almost stopped reading this thread when I came across the second of those unformatted posts; it's just not worth the time to try to puzzle out that unformatted code.

Noted. I would have felt the same. I have modified the post now.

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.