Design Patterns in Rust: Factory method, automating the factory

Introduction

In this article I discussed the implementation of the Abstract Factory pattern. The Factory Method is simply an extension of that pattern. Creating an object can sometimes be complex. A factory-method can abstract this complexity away, and let subclasses or in our case interface implementations decide which objects to create.

It looks like this:

It will all become clearer in the code. The code can be found here.

Implementation in Rust

Scroll down to just above the main() method.

The VehicleCreator can only create vehicles from two brands: Brand A and Brand B.

Now add the VehicleCreator trait:

trait VehicleCreator {
    fn create_car(&self,brand:Brand,color:String)->Result<Box<dyn AbstractCar>,VehicleError>;
    fn create_bike(&self,brand:Brand,wheels:i8)->Result<Box<dyn AbstractBike>,VehicleError>;
}

Like the VehicleFactory we create two kinds of vehicles: cars and bikes.

Next we need to provide a concrete implementation:

struct VehicleCreaterImpl;

impl VehicleCreator for VehicleCreaterImpl {
    fn create_car(&self,brand:Brand,color:String)->Result<Box<dyn AbstractCar>,VehicleError>{
        let factory=get_factory(brand);
        Ok(factory.create_car(&color)?)
    }
    fn create_bike(&self,brand:Brand,wheels:i8)->Result<Box<dyn AbstractBike>,VehicleError>{
        let factory=get_factory(brand);
        Ok(factory.create_bike(wheels)?)
    }
}

Some notes:

  • Creating a vehicle can go wrong, hence the Result-return type.
  • Because Rust has true and powerful enums, we can be sure that our match is always exhaustive i.e. the get_factory method will always return a factory.
  • The create_car() and the create_bike() method can return an error. The ? propagates this error to the caller.

Time to test

We will see that the testing code is much simpler, and also is tolerant to errors:

fn main() {
    let vehicle_creator=VehicleCreaterImpl;
    
    match vehicle_creator.create_car(Brand::BrandA,"Red".to_string()) {
        Ok(car) => println!("{}",car.description()),
        Err(e) => eprintln!("Error creating car: {}", e),
    }
    
    match vehicle_creator.create_bike(Brand::BrandB,2) {
        Ok(bike) => println!("{}",bike.description()),
        Err(e) => eprintln!("Error creating bike: {}", e),
    }
}

Some notes:

  1. We create a VehicleCreatorImpl
  2. We try to create a car. If this works Ok, we print out the description, if not we handle the error.
  3. We do the same for a bike.

Conclusion

The Factory Method pattern builds on the principles of the Abstract Factory pattern by adding flexibility to object creation. Instead of hardcoding which concrete classes to instantiate, it delegates that decision to subclasses or specific implementations. This makes your code cleaner, more extensible, and easier to maintain—especially when working with complex or evolving object hierarchies. In Rust, using traits and enums makes the pattern both powerful and type-safe, ensuring that object creation remains robust and predictable.

The Code Nomad
The Code Nomad
Articles: 165

Leave a Reply

Your email address will not be published. Required fields are marked *