This post will show you a couple of ways to implement the PartialEq trait for a custom type in Rust. This is particularly useful for testers since we end up using the assert_eq! macro.
A bit of background: Qxf2 has started adopting Rust as our primary language to write our tests and tools. The reasons for the shift from Python are many but do not fit in this post. We recently ended up writing several API tests in Rust. We are unable to share the full code with the larger testing community. But we thought we could at least share useful snippets or patterns from our experience writing the tests. In this post, we will share one pattern that will help you make your assert statements more readable.
The PartialEq trait
In most tests, we plan an assert(actual, expected) statement at the end. In Rust, the equivalent is an assert_eq!(actual, expected). For the assert_eq! to work, the types have to implement a trait called PartialEq. In Rust land, this means assert_eq! macros is ‘trait bound’ by T:PartialEq.
The simplest way: derive(PartialEq)
The simplest way to implement PartialEq on your custom type is to use the derive attribute and automatically generate PartialEq trait for your struct. You can do this by annotating your struct like so:
#[derive(PartialEq)] struct Cars{ //your code here } |
This works for almost all cases. In this post, we will see one other way to implement PartialEq that gives us a lot more control over what we compare. You will be using this technique very rarely. You can use this technique in cases where you might want to ignore certain fields in the struct during comparison or maybe introduce custom logic for how a comparison happens, etc.
Implement PartialEq for a custom type in Rust
The custom types in this example come from of our internal application cars-api that we originally wrote to help testers practice API tests.
This post has been broken down into the following pieces:
1. For one struct, we would show the PartialEq trait in derive attribute
2. For another struct, we would implement PartialEq trait
3. We would also decode the JSON from cars-app and use struct within the struct
If all this looks confusing, hang in tight, I would show it up with an interesting example 🙂
Example of using PartialEq trait in Derive attribute for a custom type
We have taken an example from the cars-app where we have tried to create a struct Car along with the following members viz. name, brand, car_type and price. The derive attribute in the Car Struct implements the PartialEq trait. The structure looks like the one below.
#[derive(Debug, PartialEq)] pub struct Car { name: String, brand: String, car_type: String, price: Price } |
Price is defined as a separate struct with the following members viz. amount and denomination
#[derive(Debug, PartialEq)] struct Price{ amount: i32, denomination: char } |
Example of implementing PartialEq trait for a custom type
In the following struct, we would see how we can implement PartialEq for another struct named RegisteredCar. The RegisteredCar would take two members as car and customer_details
#[derive(Debug)] pub struct RegisteredCar { car: Car, customer_details: CustomerDetails } |
CustomerDetails is defined as another struct with the following members viz. city and customer_name
#[derive(Debug, PartialEq)] pub struct CustomerDetails { city: String, customer_name: String } |
Now, to implement PartialEq, we do it the following way.
impl PartialEq for RegisteredCar { fn eq(&self, other: &Self) -> bool { (self.car == other.car) && (self.customer_details == other.customer_details) } } |
In the above method “eq”, the &self and &Self are references to the RegisteredCar struct which allows the method to perform the equality operation without copying the entire RegisteredCar struct.
Playing inside test
You might have realized, I have picked up a boring struct ‘Car’ with an old age member example and the cherry on the cake is the number of structs, attributes, etc. we have spoken about.
Now, I cannot think of showing a “Car” example without actually talking about the animated sports comedy film Cars and its main characters viz. Sally Carrera (The wife character) and lightning McQueen (The husband character) 😀
So, we will define Sally Carrera’s and lightning McQueen’s cars below and see how to use assert_eq! in it.
fn main() { let disney_pixar_husband = Car { name: String::from("Lightning McQueen"), brand: String::from("Chevrolet"), car_type: String::from("Sports"), price: Price{amount: 4, denomination: 'L'} }; let disney_pixar_wife = Car { name: String::from("Sally Carrera"), brand: String::from("Porsche"), car_type: String::from("Sports"), price: Price{amount: 3, denomination: 'L'} }; assert_eq!(disney_pixar_husband, disney_pixar_wife); |
The above implements the “Car” struct which have PartialEq trait and with which we can use the assert_eq! macro. Now, the members takes on different values and hence the assert_eq! would result in an error.
Now, let’s see how to use the RegisteredCar struct in main for which we have implemented PartialEq above. We will register two new cars and use assert_eq! macro on it.
On the above cartoon context, protagonist (Sally Carrera and Lightning McQueen) is boring without antagonist (Chick Hicks and Mach 5). Let’s see how two newly registered car compares with the others.
let speed_racer_villain = Car { name: String::from("Mach 5"), brand: String::from("Ferrari"), car_type: String::from("Race"), price: Price{amount: 3, denomination: 'L'} }; let disney_pixar_villain = Car { name: String::from("Chick Hicks"), brand: String::from("Shyster Cremlin"), car_type: String::from("Sports"), price: Price{amount: 4, denomination: 'L'} }; let speed_racer_villain_details = CustomerDetails { city: String::from("Tokyo"), customer_name: String::from("Tatsuo Yoshida") }; let disney_pixar_villain_details = CustomerDetails { city: String::from("Los Angeles"), customer_name: String::from("Michael Keaton") }; |
Now, woody and jessie have both registered for the above cars. Let’s use assert_eq! on them and see.
let woody = RegisteredCar { car: speed_racer_villain, customer_details: speed_racer_villain_details }; let jessie = RegisteredCar { car: disney_pixar_villain, customer_details: disney_pixar_villain_details }; assert_eq!(woody,jessie) |
If you remember, RegisteredCar is the struct we have implemented PartialEq on and you can see how we have used assert_eq! on it.
Now, the above codes have been dissected into smaller modules for better understanding. The complete code is on the next section.
The complete snippet!
I have added the complete code for this post: Implement PartialEq in Rust.
Hire Qxf2 for testing
If you are looking for good software testers who can work on Rust projects, drop us a note here. Qxf2 loves testing early stage product and we work well with small engineering teams.
My journey in software testing began with Calabash and Cucumber, where I delved into Mobile Automation using Ruby. As my career progressed, I gained experience working with a diverse range of technical tools. These include e-Discovery, Selenium, Python, Docker, 4G/5G testing, M-CORD, CI/CD implementation, Page Object Model framework, API testing, Testim, WATIR, MockLab, Postman, and Great Expectation. Recently, I’ve also ventured into learning Rust, expanding my skillset further. Additionally, I am a certified Scrum Master, bringing valuable agile expertise to projects.
On the personal front, I am a very curious person on any topic. According to Myers-Briggs Type Indicator, I am described as INFP-T. I like playing soccer, running and reading books.