# 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. ```rust # use std::marker::PhantomData; # use derive_more::{Eq, PartialEq}; # trait Trait { type Assoc; } impl Trait for T { type Assoc = u8; } #[derive(Debug, Eq, PartialEq)] struct Foo { a: A, b: PhantomData, 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: ```rust # use std::marker::PhantomData; # # trait Trait { # type Assoc; # } # impl Trait for T { # type Assoc = u8; # } # # struct Foo { # a: A, # b: PhantomData, # c: C::Assoc, # } # impl PartialEq for Foo where A: PartialEq, PhantomData: 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 Eq for Foo where Self: PartialEq, A: Eq, PhantomData: 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. ```rust # use std::marker::PhantomData; # use derive_more::{Eq, PartialEq}; # # trait Trait { # type Assoc; # } # impl Trait for T { # type Assoc = u8; # } # #[derive(Debug, Eq, PartialEq)] enum Foo { A(A), B { b: PhantomData }, C(C::Assoc), } # # #[derive(Debug)] # struct NoEq; assert_eq!(Foo::<_, NoEq, NoEq>::A(3), Foo::A(3)); assert_ne!(Foo::<_, NoEq, NoEq>::A(3), Foo::A(0)); assert_eq!(Foo::::B { b: PhantomData }, Foo::B { b: PhantomData }); assert_eq!(Foo::::C(3), Foo::C(3)); assert_ne!(Foo::::C(3), Foo::C(0)); ``` This generates code equivalent to: ```rust # use std::marker::PhantomData; # # trait Trait { # type Assoc; # } # impl Trait for T { # type Assoc = u8; # } # # enum Foo { # A(A), # B { b: PhantomData }, # C(C::Assoc), # } # impl PartialEq for Foo where A: PartialEq, PhantomData: 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 Eq for Foo where Self: PartialEq, A: Eq, PhantomData: 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. ```rust # use derive_more::{Eq, PartialEq}; # #[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: ```rust # struct NoEq; # # struct Foo { num: i32, ignored: f32 } # 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, {} # struct Bar(i32, NoEq); # impl PartialEq for Bar { fn eq(&self, __other: &Self) -> bool { true } } impl Eq for Bar {} # enum Enum { # Foo(i32, NoEq), # Bar(NoEq), # Baz, # } # 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, } } } ```