Cookie Consent by Privacy Policies Generator Vegapit
Factory type pattern using Enums in Rust
Rust Tue, 08 Dec 2020 07:49:26 +0000

Thanks to the very powerful Enum types in Rust, it is easy to define a function that can return different types in a safe manner. Let's run through a very basic example highlighting the approach.

Let's imagine two very simple struct, each representing a geometric shape: Circle or a Square. They both have a constructor that requires a single parameter of type f64, and each implements a method that the other does not, respectively area and perimeter. Here is the code:

use std::f64::consts::PI;

struct Square {
    side: f64
}

impl Square {
    pub fn new(side : f64) -> Self {
        Self{side}
    }

    pub fn perimeter(&self) -> f64 {
        4f64  * self.side
    }
}

struct Circle {
    radius: f64
}

impl Circle {
    pub fn new(radius : f64) -> Self {
        Self{radius}
    }

    pub fn area(&self) -> f64 {
        PI * self.radius.powf(2f64)
    }
}

We would like to implement a function called build_shape that takes two arguments: a string describing the shape to be built ("Circle" or "Square") and a parameter value of type f64 to pass to the corresponding constructor. The function will return an instance of the type suggested by the name. Since both structs implement different methods, we could not constrain the return type of our function to a single Trait. Instead we will declare an enum that can internally pack instances of each type (which we will call Shape) and return an instance with our function:

enum Shape {
    Circle(Circle),
    Square(Square)
}

fn build_shape(name: &str, param: f64) -> Shape {
    match name{
        "Circle" => Shape::Circle(Circle::new(param)),
        "Square" => Shape::Square(Square::new(param)),
        _ => panic!("Shape unknown")
    }
}

The Shape instance can then simply be unpacked appropriately using a match expression. Here is an example of its use in a main function:

fn main() {
    match build_shape("Circle", 1f64) {
        Shape::Circle( obj ) => println!("Area: {}", obj.area()),
        Shape::Square( obj ) => println!("Perimeter: {}", obj.perimeter())
    }
}

// Returns Area: 3.141592653589793

This Rust enum type seems very much inspired by the Algebraic Data Types that exist in Functional Programming (FP) languages like Haskell or OCaml. It is less powerful than its FP counterterparts but nonetheless very useful for handling Factory type functionality efficiently.

If you like this post, don't forget to follow me on Twitter and get notified of the next publication.