🦀 Rust: Eq vs PartialEq

tags
type
Post
summary
status
Published
slug
rust-eq-vs-partialeq
date
Apr 1, 2023
The Rust Project or Foundation has not reviewed/approved/endorsed this content. :)
In Rust, if you want to overload operators, you need to implement the corresponding traits.
For example, <, <=, >, and >= require the implementation of the PartialOrd trait:
use std::fmt::Display; struct Pair<T> { x: T, y: T, } impl<T> Pair<T> { fn new(x: T, y: T) -> Self { Self { x, y } } } impl<T: Display + PartialOrd> Pair<T> { fn cmp_display(&self) { if self.x >= self.y { println!("The largest member is x = {}", self.x); } else { println!("The largest member is y = {}", self.y); } } }
Similarly, the + operator requires the implementation of the std::ops::Add trait, and the main characters of this article, Eq and PartialEq, correspond to the == and != operators. So, what is the difference between these two traits?
We may not be very clear on this point, and even if we consult the documentation, we may not find a particularly clear explanation. If you have looked at the examples in the standard library, you may have seen this one:
enum BookFormat { Paperback, Hardback, Ebook } struct Book { isbn: i32, format: BookFormat, } impl PartialEq for Book { fn eq(&self, other: &Self) -> bool { self.isbn == other.isbn } } impl Eq for Book {}
Here, only PartialEq is implemented, and Eq is not implemented, but the default implementation impl Eq for Book {} is used directly. Strange, right? Don't worry, there's more:
impl PartialEq<IpAddr> for Ipv4Addr { #[inline] fn eq(&self, other: &IpAddr) -> bool { match other { IpAddr::V4(v4) => self == v4, IpAddr::V6(_) => false, } } } impl Eq for Ipv4Addr {}
This code comes from the Rust standard library, and as you can see, it is still used in this way, with countless similar situations. So does this mean that if we want to add equality comparison for our type, we only need to implement PartialEq?
Actually, the key point is the word "partial". If our type only has equality in certain situations, then you can only implement PartialEq, otherwise you can implement PartialEq and then use the default implementation of Eq.
Okay, the problem is gradually becoming clear, and now we just need to figure out what partial equality means.

🦀 Partial Equality

First, we need to find a type that implements PartialEq but not Eq (you may wonder if there is a reverse situation? Of course not, partial equality must be a subset of total equality!)
In the HashMap section, it was mentioned that the key of a HashMap must implement the Eq trait, which means it must be completely equal. Floating-point numbers cannot be used as the key of a HashMap because they do not implement Eq.
So, let's consider why floating-point numbers can be compared even though they do not implement Eq:
fn main() { let f1 = 3.14; let f2 = 3.14; if f1 == f2 { println!("hello, world!"); } }
We can see the output of this code, which means that floating-point numbers do implement PartialEq. Let's verify this with a simple code snippet:
fn main() { let f1 = 3.14; is_eq(f1); is_partial_eq(f1) } fn is_eq<T: Eq>(f: T) {} fn is_partial_eq<T: PartialEq>(f: T) {}
The above code uses trait constraints to verify our conclusion:
3 | is_eq(f1); | ----- ^^ the trait `Eq` is not implemented for `{float}`
Okay, since we have successfully found a type that implements PartialEq but not Eq, let's use it to see what partial equality means.
Actually, the answer is very simple. Floating-point numbers have a special value NaN that cannot be compared for equality:
fn main() { let f1 = f32::NAN; let f2 = f32::NAN; if f1 == f2 { println!("NaN can be compared, this is not mathematical!") } else { // This will be the output println!("As expected, although both are NaN, they are not equal") } }
Since floating-point numbers have a value that cannot be compared for equality, it can only implement PartialEq but not Eq. Similarly, if our type has this special requirement, we should do the same.
But why can not the NaN be compared? It’s so complicated, but there’re some useful links:
 

© Stimw 2022 - 2024