Nugget 4

Home

Comparing Things

You write if a == b and the compiler says no. Here's how to make equality work on your types using PartialEq.

The Problem

struct Point { x: i32, y: i32 }

fn main() {
    let a = Point { x: 1, y: 2 };
    let b = Point { x: 1, y: 2 };
    if a == b {                // ❌ binary operation `==` cannot be applied
        println!("same!");
    }
}

Rust doesn't assume anything about equality. You have to tell it — and that's what the PartialEq trait is for.

The Fix: #[derive(PartialEq)]

Add #[derive(PartialEq)] — Rust generates field-by-field comparison automatically:

#[derive(PartialEq)]
struct Point { x: i32, y: i32 }

fn main() {
    let a = Point { x: 1, y: 2 };
    let b = Point { x: 1, y: 2 };
    let c = Point { x: 5, y: 5 };

    println!("a == b: {}", a == b);  // ✅ true
    println!("a != c: {}", a != c);  // ✅ true
}

💡 You get != for free

Implement PartialEq and both == and != start working automatically — the trait provides the negation for you.

Nested Structs

PartialEq works recursively — every field must also implement it:

#[derive(PartialEq)]
struct Point { x: i32, y: i32 }

#[derive(PartialEq)]
struct Line { start: Point, end: Point }

fn main() {
    let a = Line {
        start: Point { x: 0, y: 0 },
        end: Point { x: 10, y: 10 },
    };
    let b = Line {
        start: Point { x: 0, y: 0 },
        end: Point { x: 10, y: 10 },
    };
    println!("{}", a == b);  // ✅ true — compares field by field by field
}

If Point didn't implement PartialEq, the derive on Line would fail at compile time. Rust checks this for you.

Custom Equality Logic

If field-by-field comparison isn't what you want, implement PartialEq by hand:

struct User {
    id: u64,
    name: String,
}

impl PartialEq for User {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id  // two users are equal if same ID
    }                         // even if names differ
}

fn main() {
    let a = User { id: 1, name: "Alice".into() };
    let b = User { id: 1, name: "Alyssa".into() };
    println!("{}", a == b);  // ✅ true — same id
}

Eq vs PartialEq

You'll often see #[derive(PartialEq, Eq)]. What's the difference?

trait PartialEq<Rhs: ?Sized = Self> {
    fn eq(&self, other: &Rhs) -> bool;

    fn ne(&self, other: &Rhs) -> bool { !self.eq(other) }
}

trait Eq: PartialEq<Self> {}  // just a marker — no methods

Eq is a marker trait that says "this type has total equality" — every value is equal to itself (reflexive). Most types qualify, but floats (f32, f64) don't — because NaN != NaN.

// f64 implements PartialEq (NaN != NaN breaks reflexivity)
//      but does NOT implement Eq

let nan = f64::NAN;
println!("{}", nan == nan);  // ❌ false — NaN is never equal to itself

// Your struct: derive PartialEq + Eq when every field is Eq
#[derive(PartialEq, Eq)]
struct Point { x: i32, y: i32 }  // i32 implements Eq

💡 When to add Eq?

Add Eq whenever PartialEq makes sense and your type doesn't contain floats. It enables types like HashMap and HashSet, which require Eq. When in doubt, #[derive(PartialEq, Eq)] is the standard practice.

Key Takeaways