How to implement Eq and PartialEq

Implement `PartialEq` for types where equality might not be defined for all values (like floating-point numbers with NaN), and implement `Eq` only when your type guarantees a total ordering where `a == b` is always true, false, or undefined in a consistent way.

Implement PartialEq for types where equality might not be defined for all values (like floating-point numbers with NaN), and implement Eq only when your type guarantees a total ordering where a == b is always true, false, or undefined in a consistent way. You can derive both traits automatically if your struct contains only comparable fields, or manually implement them to customize logic like ignoring case or specific fields.

For most standard types, the derive macro is the safest and fastest approach. This generates code that compares every field recursively.

#[derive(PartialEq, Eq, Debug)]
struct User {
    id: u32,
    name: String,
}

fn main() {
    let u1 = User { id: 1, name: "Alice".to_string() };
    let u2 = User { id: 1, name: "Alice".to_string() };
    let u3 = User { id: 1, name: "Bob".to_string() };

    assert_eq!(u1, u2); // true
    assert_ne!(u1, u3); // true
}

If you need custom logic, such as case-insensitive string comparison or ignoring a specific field, you must implement the traits manually. Note that if you implement Eq, you must also implement PartialEq first, as Eq is a marker trait that extends PartialEq.

#[derive(Debug)]
struct CaseInsensitiveString(String);

impl PartialEq for CaseInsensitiveString {
    fn eq(&self, other: &Self) -> bool {
        self.0.eq_ignore_ascii_case(&other.0)
    }
}

// Safe to implement Eq because string equality is always consistent
// and there are no "undefined" states like NaN in floats.
impl Eq for CaseInsensitiveString {}

fn main() {
    let a = CaseInsensitiveString("Hello".to_string());
    let b = CaseInsensitiveString("HELLO".to_string());
    assert_eq!(a, b);
}

Be careful with floating-point types. f32 and f64 implement PartialEq but not Eq because NaN != NaN. If you wrap a float in your own struct, you generally should not implement Eq unless you explicitly handle NaNs (e.g., treating them as equal or excluding them from the type).

#[derive(Debug)]
struct Point {
    x: f64,
    y: f64,
}

impl PartialEq for Point {
    fn eq(&self, other: &Self) -> bool {
        // Custom logic: treat NaNs as equal if both are NaN, otherwise standard comparison
        (self.x == other.x || (self.x.is_nan() && other.x.is_nan())) &&
        (self.y == other.y || (self.y.is_nan() && other.y.is_nan()))
    }
}

// Only implement Eq if you are certain your custom logic satisfies the
// requirements of total equality (reflexive, symmetric, transitive).
// For floats with NaN handling, this is often tricky and usually avoided.
// impl Eq for Point {} 

In summary, use #[derive] for standard data structures. Use manual implementation when you need semantic equality (like ignoring whitespace or case) or when wrapping types with special equality rules. Always ensure Eq is only implemented if the equality relation is mathematically total.