Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
([#459](https://github.com/JelteF/derive_more/pull/459))
- Support structs with no fields in `FromStr` derive.
([#469](https://github.com/JelteF/derive_more/pull/469))
- Add `PartialEq` derive similar to `std`'s one, but considering generics correctly.
([#473](https://github.com/JelteF/derive_more/pull/473))

### Changed

Expand Down
7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ debug = ["derive_more-impl/debug"]
deref = ["derive_more-impl/deref"]
deref_mut = ["derive_more-impl/deref_mut"]
display = ["derive_more-impl/display"]
eq = ["derive_more-impl/eq"]
error = ["derive_more-impl/error"]
from = ["derive_more-impl/from"]
from_str = ["derive_more-impl/from_str"]
Expand Down Expand Up @@ -88,6 +89,7 @@ full = [
"deref",
"deref_mut",
"display",
"eq",
"error",
"from",
"from_str",
Expand Down Expand Up @@ -213,6 +215,11 @@ name = "not"
path = "tests/not.rs"
required-features = ["not"]

[[test]]
name = "partial_eq"
path = "tests/partial_eq.rs"
required-features = ["eq"]

[[test]]
name = "sum"
path = "tests/sum.rs"
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ These are traits that can be used for operator overloading.
`BitOrAssign` and `BitXorAssign`
10. [`MulAssign`-like], contains `MulAssign`, `DivAssign`, `RemAssign`,
`ShrAssign` and `ShlAssign`
11. [`PartialEq`]


### Static methods
Expand Down Expand Up @@ -260,6 +261,7 @@ Changing [MSRV] (minimum supported Rust version) of this crate is treated as a *
[`DerefMut`]: https://docs.rs/derive_more/latest/derive_more/derive.DerefMut.html
[`AddAssign`-like]: https://docs.rs/derive_more/latest/derive_more/derive.AddAssign.html
[`MulAssign`-like]: https://docs.rs/derive_more/latest/derive_more/derive.MulAssign.html
[`PartialEq`]: https://docs.rs/derive_more/latest/derive_more/derive.PartialEq.html

[`Constructor`]: https://docs.rs/derive_more/latest/derive_more/derive.Constructor.html
[`IsVariant`]: https://docs.rs/derive_more/latest/derive_more/derive.IsVariant.html
Expand Down
2 changes: 2 additions & 0 deletions impl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ debug = ["syn/extra-traits", "dep:unicode-xid"]
deref = []
deref_mut = []
display = ["syn/extra-traits", "dep:unicode-xid", "dep:convert_case"]
eq = ["syn/extra-traits", "syn/visit"]
error = ["syn/extra-traits"]
from = ["syn/extra-traits"]
from_str = ["dep:convert_case"]
Expand All @@ -83,6 +84,7 @@ full = [
"deref",
"deref_mut",
"display",
"eq",
"error",
"from",
"from_str",
Expand Down
147 changes: 147 additions & 0 deletions impl/doc/eq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Using `#[derive(PartialEq)]`

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




## Structural equality

Deriving `PartialEq` for enums/structs works in a similar way to the one in `std`,
by comparing all the available fields, but, in the contrast, does not overconstrain
generic parameters.


### Structs

For structs all the available fields are checked for equality.

```rust
# use std::marker::PhantomData;
# use derive_more::PartialEq;
#
trait Trait {
type Assoc;
}
impl<T: ?Sized> Trait for T {
type Assoc = u8;
}

#[derive(Debug, 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:
```rust
# use std::marker::PhantomData;
#
# trait Trait {
# type Assoc;
# }
# impl<T: ?Sized> Trait for T {
# type Assoc = u8;
# }
#
# struct Foo<A, B, C: Trait + ?Sized> {
# a: A,
# b: PhantomData<B>,
# c: C::Assoc,
# }
#
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
{
#[inline]
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
}
}
}
}
```


### 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::PartialEq;
#
# trait Trait {
# type Assoc;
# }
# impl<T: ?Sized> Trait for T {
# type Assoc = u8;
# }
#
#[derive(Debug, PartialEq)]
enum Foo<A, B, C: Trait + ?Sized> {
A(A),
B { b: PhantomData<B> },
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::<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:
```rust
# use std::marker::PhantomData;
#
# trait Trait {
# type Assoc;
# }
# impl<T: ?Sized> Trait for T {
# type Assoc = u8;
# }
#
# enum Foo<A, B, C: Trait + ?Sized> {
# A(A),
# B { b: PhantomData<B> },
# C(C::Assoc),
# }
#
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
{
#[inline]
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() },
}
}
}
```
4 changes: 4 additions & 0 deletions impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ mod mul_like;
mod not_like;
#[cfg(any(feature = "debug", feature = "display"))]
pub(crate) mod parsing;
#[cfg(feature = "eq")]
mod partial_eq;
#[cfg(feature = "sum")]
mod sum_like;
#[cfg(feature = "try_from")]
Expand Down Expand Up @@ -184,6 +186,8 @@ create_derive!(
);
create_derive!("display", fmt::display, Pointer, pointer_derive, pointer);

create_derive!("eq", partial_eq, PartialEq, partial_eq_derive);

create_derive!("error", error, Error, error_derive, error);

create_derive!("from", from, From, from_derive, from);
Expand Down
Loading