Evolution from function to trait in 4 steps

struct impl trait

In our application we would like to be able to do some computation. E.g. we would like to calculate the area of a rectangle.

We see 4 different implementations

Simple function

The most simple way is to implement a function that will receive the width and length of the rectangle and return the area. Besides calling the function area there is nothing special about this function. It only multiplies two numbers.

The two parameters can be any two numbers (of the given type). They might be named width and length, but nothing really indicates that are measures of a rectangle.

This is a very generic solution, but does not convey much meaning.

examples/area-function/src/lib.rs

pub fn area(width: u64, length: u64) -> u64 {
    width * length
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let result = area(2, 3);
        assert_eq!(result, 6);
    }
}

Struct with external function

A better representation might be to define a struct called Rectangle that has two attributes for width and length. We could then call the area function from the previous implementation like this:

let r = Rectangle { width: 2, length: 3 };
let result = area(r.width, r.length);

It is probably cleared if the area function receives an instance of Rectangle (or better yet a reference to an instance)

examples/area-struct-and-function/src/lib.rs

#![allow(dead_code)]


struct Rectangle {
    width: u64,
    length: u64,
}

fn area(rect: &Rectangle) -> u64 {
    rect.width * rect.length
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let r = Rectangle { width: 2, length: 3 };
        let result = area(&r);
        assert_eq!(result, 6);
    }
}

This brings us to a generic function floating in our namespace with a very specific application. It can only be used with a Rectangle.

Struct with method

Instead of defining a function outside of the struct we could define it as a method of the Rectangle struct. We need to wrap the function definition it with impl Rectangle { } and replace the parameter that was rect: &Rectangle by &self which is the generic representation of an instance of the current struct.

Also in the test just as in general use-case, instead of passing in a reference to the function area(&r), we use the dot-notation to call it as a method: r.area().

examples/area-struct-and-method/src/lib.rs

#![allow(dead_code)]


struct Rectangle {
    width: u64,
    length: u64,
}

impl Rectangle {
    fn area(&self) -> u64 {
        self.width * self.length
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let r = Rectangle { width: 2, length: 3 };
        let result = r.area();
        assert_eq!(result, 6);
    }
}

Struct and Trait

To allow for easy generalization of the area method we could define a Trait that defined the signature of the area function: It receives a reference to an instance of the current struct and returns a u64.

pub trait Area {
    fn area(&self) -> u64;
}

Then we implement the trait using the impl Area for Rectangle expression instead of the impl Rectangle expression we had earlier.

The rest of the code does not need to change, but now we defined the idea of having a method called area which we'll be able to implement for any shape and convey the message that thy have something common.

examples/area-struct-and-trait/src/lib.rs

#![allow(dead_code)]


struct Rectangle {
    width: u64,
    length: u64,
}

pub trait Area {
    fn area(&self) -> u64;
}

impl Area for Rectangle {
    fn area(&self) -> u64 {
        self.width * self.length
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let r = Rectangle { width: 2, length: 3 };
        let result = r.area();
        assert_eq!(result, 6);
    }
}

Author

Gabor Szabo (szabgab)

Gabor Szabo, the author of the Rust Maven web site maintains several Open source projects in Rust and while he still feels he has tons of new things to learn about Rust he already offers training courses in Rust and still teaches Python, Perl, git, GitHub, GitLab, CI, and testing.

Gabor Szabo