Good article, but one (very minor) nit I have is with the PizzaOrder example.
struct PizzaOrder {
size: PizzaSize,
toppings: Vec<Topping>,
crust_type: CrustType,
ordered_at: SystemTime,
}
The problem they want to address is partial equality when you want to compare orders but ignoring the ordered_at timestamp. To me, the problem is throwing too many unrelated concerns into one struct. Ideally instead of using destructuring to compare only the specific fields you care about, you'd decompose this into two structs: #[derive(PartialEq, Eq)]
struct PizzaDetails {
size: PizzaSize,
toppings: Vec<Topping>,
crust_type: CrustType,
… // additional fields
}
#[derive(Eq)]
struct PizzaOrder {
details: PizzaDetails,
ordered_at: SystemTime,
}
impl PartialEq for PizzaOrder {
fn eq(&self, rhs: &Self) -> bool {
self.details == rhs.details
}
}
I get that this is a toy example meant to illustrate the point; there are certainly more complex cases where there's no clean boundary to split your struct across. But this should be the first tool you reach for.Decomposing things just to have different equality notions doesn't generalize.
How would you decompose a character string so that you could have a case-insensitive versus sensitive comparison?
:)
You have a good point there, that is better. But it is still, well honestly, wrong. Two orders ordered at different times are just not the same order, and using a typeclass approach to say that they most definitely are is going to bite you in the back seat.
PartialEq and Eq for PizzaDetails is good. If there is a business function that computes whether or not someone orders the same thing, then that should start by projecting the details.