This post is about the Command Design Pattern in Rust.
Command Design Pattern
This Design Pattern uses commands to wrap and invoke capabilities. A car can run faster and slower. We can have commands AccelerateCommand and DecelerateCommand for increasing and decreasing speed, respectively. We can also have AccelerateAndDecelerateCommand to run both capabilities in a particular order. Please read Command Pattern for more details.
Design Pattern Implementation
Our example is about cars and what they can do. A car can Startup and Shutdown. It can also increase or decrease its speed. Moreover, it can Turn Left and Turn Right. We can represent cars in Rust as follow.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | struct Car { make: String } impl Car { fn startup(&self) { println!("Car is starting"); } fn shutdown(&self) { println!("Car is shutting down"); } fn accelerate(&self) { println!("Car is speeding up!"); } fn decelerate(&self) { println!("Car is slowing down!"); } fn turn_left(&self) { println!("Car is turning left"); } fn turn_right(&self) { println!("Car is turning right"); } fn reverse(&self) { println!("Car is reversing"); } fn stop(&self) { println!("Car is stopping"); } fn new(p_make: String) -> Car { Car { make: p_make } } } |
Structs and Command Trait in Rust
We will have a struct that implements the Command trait for each function in the Car struct.
1 2 3 | trait Command { fn execute(&self); } |
Startup Command
This capability enables a car to start up its engine.
1 2 3 4 5 6 7 8 9 | struct CarStartUpCommand<'a> { car: &'a Car } impl<'a> Command for CarStartUpCommand<'a> { fn execute(&self) { self.car.startup(); } } |
Shutdown Command
Shutdown enables a car to turn its engine off.
1 2 3 4 5 6 7 8 9 | struct CarShutdownCommand<'a> { car: &'a Car } impl<'a> Command for CarShutdownCommand<'a> { fn execute(&self) { self.car.shutdown(); } } |
Accelerate Command
When a car goes faster, it accelerates.
1 2 3 4 5 6 7 8 9 | struct CarAccelerateCommand<'a> { car: &'a Car } impl<'a> Command for CarAccelerateCommand<'a> { fn execute(&self) { self.car.accelerate(); } } |
Decelerate Command
When a car goes slower, it decelerates.
1 2 3 4 5 6 7 8 9 | struct CarDecelerateCommand<'a> { car: &'a Car } impl<'a> Command for CarDecelerateCommand<'a> { fn execute(&self) { self.car.decelerate(); } } |
Turn Left Command
This capability allows the driver to change direction.
1 2 3 4 5 6 7 8 9 | struct CarTurnLeftCommand<'a> { car: &'a Car } impl<'a> Command for CarTurnLeftCommand<'a> { fn execute(&self) { self.car.turn_left(); } } |
Turn Right Command
This capability allows the driver to change direction.
1 2 3 4 5 6 7 8 | struct CarTurnRightCommand<'a> { car: &'a Car } impl<'a> Command for CarTurnRightCommand<'a> { fn execute(&self) { self.car.turn_right(); } } |
Reverse Command
The CarReverseCommand struct allows a car to run in reverse, which is typically is for parking.
1 2 3 4 5 6 7 8 9 | struct CarReverseCommand<'a> { car: &'a Car } impl<'a> Command for CarReverseCommand<'a> { fn execute(&self) { self.car.reverse(); } } |
Stop Command
Lastly, this capability allows a car to stop.
1 2 3 4 5 6 7 8 9 | struct CarStopCommand<'a> { car: &'a Car } impl<'a> Command for CarStopCommand<'a> { fn execute(&self) { self.car.stop(); } } |
Command Pattern Usage
Now consider we are going somewhere. We could use the car in some arranged ways to reach a destination. First, we start it up. Then, the increase in speed. Then, the decrease in speed before making turns, etc.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | fn main() { let toyota_car = Car::new("Toyota".to_string()); let mut commands_queue:Vec<Box<dyn Command>> = Vec::new(); commands_queue.push(Box::new(CarStartUpCommand { car: &toyota_car})); commands_queue.push(Box::new(CarAccelerateCommand { car: &toyota_car})); commands_queue.push(Box::new(CarDecelerateCommand { car: &toyota_car})); commands_queue.push(Box::new(CarTurnLeftCommand { car: &toyota_car})); commands_queue.push(Box::new(CarTurnRightCommand { car: &toyota_car})); commands_queue.push(Box::new(CarAccelerateCommand { car: &toyota_car})); commands_queue.push(Box::new(CarDecelerateCommand { car: &toyota_car})); commands_queue.push(Box::new(CarStopCommand { car: &toyota_car})); commands_queue.push(Box::new(CarReverseCommand { car: &toyota_car})); commands_queue.push(Box::new(CarStopCommand { car: &toyota_car})); commands_queue.push(Box::new(CarShutdownCommand { car: &toyota_car})); for command in commands_queue.iter() { command.execute(); } } |
Output:
1 2 3 4 5 6 7 8 9 10 11 | Car is starting Car is speeding up! Car is slowing down! Car is turning left Car is turning right Car is speeding up! Car is slowing down! Car is stopping Car is reversing Car is stopping Car is shutting down |
Tested with Rust 1.39.0.
That’s it! That’s how to use Command Design Pattern in Rust. The beauty of this pattern is it allows for uniformity by using a common Command trait. We could be more specific with our codes and come up with different types of cars. We could have more commands but still, have the same core codes to process them.