Operator Overloading in Rust

Stefanos Kouroupis - Oct 11 '19 - - Dev Community

This is my first post about Rust, So I'm going to keep it simple and at the same time a bit unrealistic, just to prove some points. ;-)

This article, as the title already revealed, is all about operator overloading. Specifically we will implement the following 4 traits.

  • PartialEq
  • PartialOrd
  • Add
  • Not

Our example, as other similar tutorials will be based upon a little bit of Geometry, specifically Points and Lines.

so we need to use the following

use std::cmp::Ordering;
use std::ops::Add;
use std::ops::Not;
Enter fullscreen mode Exit fullscreen mode
  • a Point will be a pair of cartesian coordinates (x,y)
#[derive(Copy, Clone)]
pub struct Point {
    pub x: f32,
    pub y: f32,
}
Enter fullscreen mode Exit fullscreen mode
  • a Line will be a pair of Points
#[derive(Copy, Clone)]
pub struct Line {
    pub start: Point,
    pub end: Point,
}
Enter fullscreen mode Exit fullscreen mode
  • The Point trait will just have a constructor
pub trait PointProperties {
    fn new(x: f32, y: f32) -> Self;
}

impl PointProperties for Point {
    fn new(x: f32, y: f32) -> Self {
        return Self { x: x, y: y };
    }
}
Enter fullscreen mode Exit fullscreen mode
  • The Line trait will have a constructor and a function that calculates the length of the Line. The length is calculated with the distance formula ( √(b.x-a.x)^2 + (b.y-a.y)^2 ) that (I assume) we all know from school.

pub trait LineProperties {
    fn length(&self) -> f32;
    fn new(a: Point, b: Point) -> Self;
}

impl LineProperties for Line {
    fn length(&self) -> f32 {
        return ((&self.end.x - &self.start.x).powf(2.0) + (&self.end.y - &self.start.y).powf(2.0))
            .sqrt();
    }
    fn new(a: Point, b: Point) -> Self {
        return Self { start: a, end: b };
    }
}
Enter fullscreen mode Exit fullscreen mode

First we will implement the equality. What is equality between two lines? In this case I am choosing to consider equal two lines that have the same length, regardless where they lie in our cartesian plane.

impl PartialEq for Line {
    fn eq(&self, other: &Self) -> bool {
        return &self.length() == &other.length();
    }
}
Enter fullscreen mode Exit fullscreen mode

Then we have the less than and greater than symbols. Again how can we compare two lines? Again I am choosing to compare the lengths of two lines, again regardless where they line in our cartesian plane

impl PartialOrd for Line {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.length().partial_cmp(&other.length())
    }
}
Enter fullscreen mode Exit fullscreen mode

Finally we down to the more unrealistic but handy nevertheless examples.

The not operator (!). In this case when applying the not operator in a line, flips the line along the y axis. This is achieved by simply reversing the signs of all cartesian coordinates that make up our line.

impl Not for Line {
    type Output = Line;
    fn not(mut self) -> Line {
        self.start.x = -self.start.x;
        self.start.y = -self.start.y;
        self.end.x = -self.end.x;
        self.end.y = -self.end.y;

        return self;
    }
}
Enter fullscreen mode Exit fullscreen mode

Finally I implement the add operator (+). Again adding two lines doesn't make much sense, so what I am doing with the add operator is that I take the min_x, min_y and max_x, max_y for each pair of coordinates (start vs end)

impl Add for Line {
    type Output = Line;
    fn add(self, other: Self) -> Line {
        let min_start_x: f32;
        if &self.start.x > &other.start.x {
            min_start_x = other.start.x;
        } else {
            min_start_x = self.start.x;
        }

        let min_start_y: f32;
        if &self.start.x > &other.start.x {
            min_start_y = other.start.y;
        } else {
            min_start_y = self.start.y;
        }

        let max_end_x: f32;
        if &self.start.x < &other.start.x {
            max_end_x = other.start.x;
        } else {
            max_end_x = self.start.x;
        }

        let max_end_y: f32;
        if &self.start.x < &other.start.x {
            max_end_y = other.start.y;
        } else {
            max_end_y = self.start.y;
        }

        let p_a = Point::new(min_start_x, min_start_y);
        let p_b = Point::new(max_end_x, max_end_y);

        return Line::new(p_a, p_b);
    }
}
Enter fullscreen mode Exit fullscreen mode

and too prove that everything is working this is our main function

fn main() {
    println!("Points & Lines");
    let point_a: Point = Point::new(0.0, 0.0);
    let point_b: Point = Point::new(0.0, 10.0);
    let line_a: Line = Line::new(point_a, point_b);

    println!("line a has a length of {:?}", line_a.length());

    let point_c: Point = Point::new(10.0, 10.0);
    let point_d: Point = Point::new(10.0, 20.0);
    let line_b: Line = Line::new(point_c, point_d);

    println!("line b has a length of  {:?}", line_b.length());
    println!("are line a and b lengths equal? {:?}", line_a == line_b);
    println!(
        "is the mirror line's a length equal to line b {:?}",
        !line_a == line_b
    );

    let line_c: Line = Line::new(point_a, point_d);
    let line_d: Line = Line::new(point_b, point_c);

    println!("line c has a length of {:?}", line_c.length());
    println!("line d has a length of {:?}", line_d.length());
    println!("is line c smaller than line b? {:?}", line_c < line_b);

    println!(
        "adding up lines give you a length of {:?}",
        (line_c + line_b).length()
    );
}
Enter fullscreen mode Exit fullscreen mode

and our output

Points & Lines
line a has a length of 10.0
line b has a length of  10.0
are line a and b lengths equal? true
is the mirror line's a length equal to line b true
line c has a length of 22.36068
line d has a length of 10.0
is line c smaller than line b? false
adding up lines give you a length of 14.142136
Enter fullscreen mode Exit fullscreen mode

I hope someone, out there, to find this piece useful. I certainly enjoyed writing it.

. . . . . . . . . . . . . . . . . . . . . . . . .