Eq

Derive Macro Eq 

Source
#[derive(Eq)]
{
    // Attributes available to this derive:
    #[eq]
}
Available on crate feature eq only.
Expand description

§Using #[derive(Eq)] and #[derive(PartialEq)]

Deriving Eq/PartialEq works by checking whether two values are equal according to their type structure.

§Structural equality

Deriving Eq/PartialEq for enums/structs works in a similar way to the one in std, by comparing all the available fields, but, in the contrast:

  1. Does not overconstrain generic parameters.
  2. Implements PartialEq::ne() method as well, to propagate possible efficient implementations of this method from the underlying types.

§Structs

For structs all the available fields are checked for equality.

trait Trait {
    type Assoc;
}
impl<T: ?Sized> Trait for T {
    type Assoc = u8;
}

#[derive(Debug, Eq, PartialEq)]
struct Foo<A, B, C: Trait + ?Sized> {
    a: A,
    b: PhantomData<B>,
    c: C::Assoc,
}

#[derive(Debug)]
struct NoEq;

assert_eq!(Foo::<_, NoEq, NoEq> { a: 3, b: PhantomData, c: 0 }, Foo { a: 3, b: PhantomData, c: 0 });
assert_ne!(Foo::<_, NoEq, NoEq> { a: 3, b: PhantomData, c: 0 }, Foo { a: 0, b: PhantomData, c: 3 });

This generates code equivalent to:

impl<A, B, C: Trait + ?Sized> PartialEq for Foo<A, B, C>
where
    A: PartialEq,
    PhantomData<B>: PartialEq, // `B: PartialEq` is generated by `std` instead
    C::Assoc: PartialEq,       // `C: PartialEq` is generated by `std` instead
{
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Self { a: self_0, b: self_1, c: self_2 }, Self { a: other_0, b: other_1, c: other_2 }) => {
                self_0 == other_0 && self_1 == other_1 && self_2 == other_2
            }
        }
    }
    fn ne(&self, other: &Self) -> bool {
        match (self, other) {
            (Self { a: self_0, b: self_1, c: self_2 }, Self { a: other_0, b: other_1, c: other_2 }) => {
                self_0 != other_0 || self_1 != other_1 || self_2 != other_2
            }
        }
    }
}

impl<A, B, C: Trait + ?Sized> Eq for Foo<A, B, C>
where
    Self: PartialEq,
    A: Eq,
    PhantomData<B>: Eq, // `B: Eq` is generated by `std` instead
    C::Assoc: Eq,       // `C: Eq` is generated by `std` instead
{}

§Enums

For enums the first check is whether these two values represent the same variant, and after that we check fields equality.

#[derive(Debug, Eq, PartialEq)]
enum Foo<A, B, C: Trait + ?Sized> {
    A(A),
    B { b: PhantomData<B> },
    C(C::Assoc),
}

assert_eq!(Foo::<_, NoEq, NoEq>::A(3), Foo::A(3));
assert_ne!(Foo::<_, NoEq, NoEq>::A(3), Foo::A(0));

assert_eq!(Foo::<u16, NoEq, NoEq>::B { b: PhantomData }, Foo::B { b: PhantomData });

assert_eq!(Foo::<i32, NoEq, NoEq>::C(3), Foo::C(3));
assert_ne!(Foo::<i32, NoEq, NoEq>::C(3), Foo::C(0));

This generates code equivalent to:

impl<A, B, C: Trait + ?Sized> PartialEq for Foo<A, B, C>
where
    A: PartialEq,
    PhantomData<B>: PartialEq, // `B: PartialEq` is generated by `std` instead
    C::Assoc: PartialEq,       // `C: PartialEq` is generated by `std` instead
{
    fn eq(&self, other: &Self) -> bool {
        std::mem::discriminant(self) == std::mem::discriminant(other) &&
            match (self, other) {
                (Self::A(self_0), Self::A(other_0)) => { self_0 == other_0 }
                (Self::B { b: self_0 }, Self::B { b: other_0 }) => { self_0 == other_0 }
                (Self::C(self_0), Self::C(other_0)) => { self_0 == other_0 }
                _ => unsafe { std::hint::unreachable_unchecked() }
            }
    }
    fn ne(&self, other: &Self) -> bool {
        std::mem::discriminant(self) != std::mem::discriminant(other) ||
            match (self, other) {
                (Self::A(self_0), Self::A(other_0)) => { self_0 != other_0 }
                (Self::B { b: self_0 }, Self::B { b: other_0 }) => { self_0 != other_0 }
                (Self::C(self_0), Self::C(other_0)) => { self_0 != other_0 }
                _ => unsafe { std::hint::unreachable_unchecked() }
            }
    }
}

impl<A, B, C: Trait + ?Sized> Eq for Foo<A, B, C>
where
    Self: PartialEq,
    A: Eq,
    PhantomData<B>: Eq, // `B: Eq` is generated by `std` instead
    C::Assoc: Eq,       // `C: Eq` is generated by `std` instead
{}

§Ignoring

Both #[eq(skip)] and #[partial_eq(skip)] attributes could be used to ignore fields, a whole struct or enum variants in the expansion.

#[derive(Debug)]
struct NoEq; // doesn't implement `Eq`/`PartialEq`

#[derive(Debug, Eq, PartialEq)]
struct Foo {
    num: i32,
    // This attribute is seen by both `Eq` and `PartialEq` macros,
    // so the repetition is unnecessary.
    #[eq(skip)] // or #[eq(ignore)]
    ignored: f32,
}

#[derive(Debug, Eq, PartialEq)]
// This attribute is also seen by both `Eq` and `PartialEq` macros.
// Makes all fields of this struct being ignored.
#[partial_eq(skip)] // or #[partial_eq(ignore)]
struct Bar(f32, NoEq);

#[derive(Debug, Eq, PartialEq)]
enum Enum {
    Foo(i32, #[eq(skip)] NoEq),
    #[eq(skip)]
    Bar(NoEq),
    Baz,
}

assert_eq!(Foo { num: 0, ignored: 1.0 }, Foo { num: 0, ignored: 1.0 });
assert_eq!(Foo { num: 0, ignored: 1.0 }, Foo { num: 0, ignored: 2.0 });
assert_ne!(Foo { num: 0, ignored: 1.0 }, Foo { num: 1, ignored: 1.0 });

assert_eq!(Bar(0.0, NoEq), Bar(0.0, NoEq));
assert_eq!(Bar(0.0, NoEq), Bar(1.0, NoEq));

assert_eq!(Enum::Foo(0, NoEq), Enum::Foo(0, NoEq));
assert_ne!(Enum::Foo(0, NoEq), Enum::Foo(1, NoEq));

assert_eq!(Enum::Bar(NoEq), Enum::Bar(NoEq));

assert_eq!(Enum::Baz, Enum::Baz);

// NOTE: Different variants are still not equal despite being ignored!
//       Ignoring a variant only ignores all its fields during comparison.
assert_ne!(Enum::Foo(0, NoEq), Enum::Bar(NoEq));
assert_ne!(Enum::Foo(0, NoEq), Enum::Baz);
assert_ne!(Enum::Bar(NoEq), Enum::Baz);

This generates code equivalent to:

impl PartialEq for Foo {
    fn eq(&self, __other: &Self) -> bool {
        match (self, __other) {
            (Self { num: __self_0, .. }, Self { num: __other_0, .. }) => { __self_0 == __other_0 }
        }
    }
    fn ne(&self, __other: &Self) -> bool {
        match (self, __other) {
            (Self { num: __self_0, .. }, Self { num: __other_0, .. }) => { __self_0 != __other_0 }
        }
    }
}
impl Eq for Foo
where
    i32: Eq,
{}

impl PartialEq for Bar {
    fn eq(&self, __other: &Self) -> bool { true }
}
impl Eq for Bar {}

impl PartialEq for Enum {
    fn eq(&self, __other: &Self) -> bool {
        std::mem::discriminant(self) == std::mem::discriminant(__other) &&
            match (self, __other) {
                (Self::Foo(__self_0, _, ), Self::Foo(__other_0, _, )) => { __self_0 == __other_0 }
                _ => true,
            }
    }
    fn ne(&self, __other: &Self) -> bool {
        std::mem::discriminant(self) != std::mem::discriminant(__other) ||
            match (self, __other) {
                (Self::Foo(__self_0, _, ), Self::Foo(__other_0, _, )) => { __self_0 != __other_0 }
                _ => false,
        }
    }
}