Skip to content

Commit 47d8c10

Browse files
authored
Bootstrap derive(PartialEq) (#473, #163)
Part of #163 ## Synopsis This PR is the initial part of #163 and adds only structural expansion of `derive(PartialEq)`. ## Solution Mirrors the `PartialEq` expansion of `std`, but applies trait bounds for generics correctly.
1 parent 07dc39d commit 47d8c10

File tree

12 files changed

+888
-2
lines changed

12 files changed

+888
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1919
([#459](https://github.com/JelteF/derive_more/pull/459))
2020
- Support structs with no fields in `FromStr` derive.
2121
([#469](https://github.com/JelteF/derive_more/pull/469))
22+
- Add `PartialEq` derive similar to `std`'s one, but considering generics correctly.
23+
([#473](https://github.com/JelteF/derive_more/pull/473))
2224

2325
### Changed
2426

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ debug = ["derive_more-impl/debug"]
6161
deref = ["derive_more-impl/deref"]
6262
deref_mut = ["derive_more-impl/deref_mut"]
6363
display = ["derive_more-impl/display"]
64+
eq = ["derive_more-impl/eq"]
6465
error = ["derive_more-impl/error"]
6566
from = ["derive_more-impl/from"]
6667
from_str = ["derive_more-impl/from_str"]
@@ -88,6 +89,7 @@ full = [
8889
"deref",
8990
"deref_mut",
9091
"display",
92+
"eq",
9193
"error",
9294
"from",
9395
"from_str",
@@ -213,6 +215,11 @@ name = "not"
213215
path = "tests/not.rs"
214216
required-features = ["not"]
215217

218+
[[test]]
219+
name = "partial_eq"
220+
path = "tests/partial_eq.rs"
221+
required-features = ["eq"]
222+
216223
[[test]]
217224
name = "sum"
218225
path = "tests/sum.rs"

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ These are traits that can be used for operator overloading.
123123
`BitOrAssign` and `BitXorAssign`
124124
10. [`MulAssign`-like], contains `MulAssign`, `DivAssign`, `RemAssign`,
125125
`ShrAssign` and `ShlAssign`
126+
11. [`PartialEq`]
126127

127128

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

264266
[`Constructor`]: https://docs.rs/derive_more/latest/derive_more/derive.Constructor.html
265267
[`IsVariant`]: https://docs.rs/derive_more/latest/derive_more/derive.IsVariant.html

impl/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ debug = ["syn/extra-traits", "dep:unicode-xid"]
5757
deref = []
5858
deref_mut = []
5959
display = ["syn/extra-traits", "dep:unicode-xid", "dep:convert_case"]
60+
eq = ["syn/extra-traits", "syn/visit"]
6061
error = ["syn/extra-traits"]
6162
from = ["syn/extra-traits"]
6263
from_str = ["dep:convert_case"]
@@ -83,6 +84,7 @@ full = [
8384
"deref",
8485
"deref_mut",
8586
"display",
87+
"eq",
8688
"error",
8789
"from",
8890
"from_str",

impl/doc/eq.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Using `#[derive(PartialEq)]`
2+
3+
Deriving `PartialEq` works by checking whether two values are equal according to
4+
their type structure.
5+
6+
7+
8+
9+
## Structural equality
10+
11+
Deriving `PartialEq` for enums/structs works in a similar way to the one in `std`,
12+
by comparing all the available fields, but, in the contrast, does not overconstrain
13+
generic parameters.
14+
15+
16+
### Structs
17+
18+
For structs all the available fields are checked for equality.
19+
20+
```rust
21+
# use std::marker::PhantomData;
22+
# use derive_more::PartialEq;
23+
#
24+
trait Trait {
25+
type Assoc;
26+
}
27+
impl<T: ?Sized> Trait for T {
28+
type Assoc = u8;
29+
}
30+
31+
#[derive(Debug, PartialEq)]
32+
struct Foo<A, B, C: Trait + ?Sized> {
33+
a: A,
34+
b: PhantomData<B>,
35+
c: C::Assoc,
36+
}
37+
38+
#[derive(Debug)]
39+
struct NoEq;
40+
41+
assert_eq!(Foo::<_, NoEq, NoEq> { a: 3, b: PhantomData, c: 0 }, Foo { a: 3, b: PhantomData, c: 0 });
42+
assert_ne!(Foo::<_, NoEq, NoEq> { a: 3, b: PhantomData, c: 0 }, Foo { a: 0, b: PhantomData, c: 3 });
43+
```
44+
This generates code equivalent to:
45+
```rust
46+
# use std::marker::PhantomData;
47+
#
48+
# trait Trait {
49+
# type Assoc;
50+
# }
51+
# impl<T: ?Sized> Trait for T {
52+
# type Assoc = u8;
53+
# }
54+
#
55+
# struct Foo<A, B, C: Trait + ?Sized> {
56+
# a: A,
57+
# b: PhantomData<B>,
58+
# c: C::Assoc,
59+
# }
60+
#
61+
impl<A, B, C: Trait + ?Sized> PartialEq for Foo<A, B, C>
62+
where
63+
A: PartialEq,
64+
PhantomData<B>: PartialEq, // `B: PartialEq` is generated by `std` instead
65+
C::Assoc: PartialEq, // `C: PartialEq` is generated by `std` instead
66+
{
67+
#[inline]
68+
fn eq(&self, other: &Self) -> bool {
69+
match (self, other) {
70+
(Self { a: self_0, b: self_1, c: self_2 }, Self { a: other_0, b: other_1, c: other_2 }) => {
71+
self_0 == other_0 && self_1 == other_1 && self_2 == other_2
72+
}
73+
}
74+
}
75+
}
76+
```
77+
78+
79+
### Enums
80+
81+
For enums the first check is whether these two values represent the same variant,
82+
and after that we check fields equality.
83+
84+
```rust
85+
# use std::marker::PhantomData;
86+
# use derive_more::PartialEq;
87+
#
88+
# trait Trait {
89+
# type Assoc;
90+
# }
91+
# impl<T: ?Sized> Trait for T {
92+
# type Assoc = u8;
93+
# }
94+
#
95+
#[derive(Debug, PartialEq)]
96+
enum Foo<A, B, C: Trait + ?Sized> {
97+
A(A),
98+
B { b: PhantomData<B> },
99+
C(C::Assoc),
100+
}
101+
#
102+
# #[derive(Debug)]
103+
# struct NoEq;
104+
105+
assert_eq!(Foo::<_, NoEq, NoEq>::A(3), Foo::A(3));
106+
assert_ne!(Foo::<_, NoEq, NoEq>::A(3), Foo::A(0));
107+
108+
assert_eq!(Foo::<u16, NoEq, NoEq>::B { b: PhantomData }, Foo::B { b: PhantomData });
109+
110+
assert_eq!(Foo::<i32, NoEq, NoEq>::C(3), Foo::C(3));
111+
assert_ne!(Foo::<i32, NoEq, NoEq>::C(3), Foo::C(0));
112+
```
113+
This generates code equivalent to:
114+
```rust
115+
# use std::marker::PhantomData;
116+
#
117+
# trait Trait {
118+
# type Assoc;
119+
# }
120+
# impl<T: ?Sized> Trait for T {
121+
# type Assoc = u8;
122+
# }
123+
#
124+
# enum Foo<A, B, C: Trait + ?Sized> {
125+
# A(A),
126+
# B { b: PhantomData<B> },
127+
# C(C::Assoc),
128+
# }
129+
#
130+
impl<A, B, C: Trait + ?Sized> PartialEq for Foo<A, B, C>
131+
where
132+
A: PartialEq,
133+
PhantomData<B>: PartialEq, // `B: PartialEq` is generated by `std` instead
134+
C::Assoc: PartialEq, // `C: PartialEq` is generated by `std` instead
135+
{
136+
#[inline]
137+
fn eq(&self, other: &Self) -> bool {
138+
std::mem::discriminant(self) == std::mem::discriminant(other) &&
139+
match (self, other) {
140+
(Self::A(self_0), Self::A(other_0)) => { self_0 == other_0 }
141+
(Self::B { b: self_0 }, Self::B { b: other_0 }) => { self_0 == other_0 }
142+
(Self::C(self_0), Self::C(other_0)) => { self_0 == other_0 }
143+
_ => unsafe { std::hint::unreachable_unchecked() },
144+
}
145+
}
146+
}
147+
```

impl/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ mod mul_like;
5757
mod not_like;
5858
#[cfg(any(feature = "debug", feature = "display"))]
5959
pub(crate) mod parsing;
60+
#[cfg(feature = "eq")]
61+
mod partial_eq;
6062
#[cfg(feature = "sum")]
6163
mod sum_like;
6264
#[cfg(feature = "try_from")]
@@ -184,6 +186,8 @@ create_derive!(
184186
);
185187
create_derive!("display", fmt::display, Pointer, pointer_derive, pointer);
186188

189+
create_derive!("eq", partial_eq, PartialEq, partial_eq_derive);
190+
187191
create_derive!("error", error, Error, error_derive, error);
188192

189193
create_derive!("from", from, From, from_derive, from);

0 commit comments

Comments
 (0)