loading...

Operator Overloading in Rust

elasticrash profile image Stefanos Kouroupis ・4 min read

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;
  • a Point will be a pair of cartesian coordinates (x,y)
#[derive(Copy, Clone)]
pub struct Point {
    pub x: f32,
    pub y: f32,
}
  • a Line will be a pair of Points
#[derive(Copy, Clone)]
pub struct Line {
    pub start: Point,
    pub end: Point,
}
  • 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 };
    }
}
  • 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 };
    }
}

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();
    }
}

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())
    }
}

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;
    }
}

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);
    }
}

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()
    );
}

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

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

Discussion

pic
Editor guide