Skip to content

Commit 2d3805b

Browse files
Allow skipping fields for Add/AddAssign/Mul/MulAssign-like derives (#472, #437, #138)
- fully rework `Add`/`AddAssign`/`Mul`/`MulAssign`-like derive macros - infer trait bounds for generics structurally in `Add`/`AddAssign`/`Mul`/`MulAssign`-like derive macros Co-authored-by: Kai Ren <tyranron@gmail.com>
1 parent 1b5d314 commit 2d3805b

File tree

89 files changed

+3452
-939
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+3452
-939
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
3333
([#494](https://github.com/JelteF/derive_more/pull/494))
3434
- Support custom error in `TryInto` derive.
3535
([#503](https://github.com/JelteF/derive_more/pull/503))
36+
- Support skipping fields in `Add`-like, `AddAssign`-like, `Mul`-like and
37+
`MulAssign`-like derives.
38+
([#472](https://github.com/JelteF/derive_more/pull/472))
3639

3740
### Changed
3841

3942
- The minimum supported Rust version (MSRV) is now Rust 1.81.
4043
([#466](https://github.com/JelteF/derive_more/pull/466))
44+
- `Add`-like, `AddAssign`-like, `Mul`-like and `MulAssign`-like derives now
45+
infer trait bounds for generics structurally (bound field types instead of
46+
type parameters directly).
47+
([#472](https://github.com/JelteF/derive_more/pull/472))
4148

4249
### Fixed
4350

impl/Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ unexpected_cfgs = { level = "warn", check-cfg = ["cfg(ci)", "cfg(error_generic_m
4949
[features]
5050
default = []
5151

52-
add = []
53-
add_assign = []
52+
add = ["syn/extra-traits", "syn/visit"]
53+
add_assign = ["syn/extra-traits", "syn/visit"]
5454
as_ref = ["syn/extra-traits", "syn/visit"]
5555
constructor = []
5656
debug = ["syn/extra-traits", "dep:unicode-xid"]
@@ -66,8 +66,8 @@ index_mut = []
6666
into = ["syn/extra-traits", "syn/visit-mut"]
6767
into_iterator = []
6868
is_variant = ["dep:convert_case"]
69-
mul = ["syn/extra-traits"]
70-
mul_assign = ["syn/extra-traits"]
69+
mul = ["syn/extra-traits", "syn/visit"]
70+
mul_assign = ["syn/extra-traits", "syn/visit"]
7171
not = ["syn/extra-traits"]
7272
sum = []
7373
try_from = []

impl/doc/add.md

Lines changed: 108 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,100 @@
11
# What `#[derive(Add)]` generates
22

3-
The derived `Add` implementation will allow two structs of the same type to be
4-
added together. This is done by adding their respective fields together and
5-
creating a new struct with those values.
6-
For enums each variant can be added in a similar way to another instance of that
7-
same variant. There's one big difference however: it returns a
8-
`Result<EnumType>`, because an error is returned when two different variants are
9-
added together.
3+
> **NOTE**: `Sub`, `BitAnd`, `BitOr` and `BitXor` derives are fully equivalent
4+
> to the `Add` derive described below.
5+
6+
Deriving `Add` works by adding two values structurally (field by field, according
7+
to their type structure).
108

119

1210

1311

14-
## Tuple structs
12+
## Structs
1513

16-
When deriving `Add` for a tuple struct with two fields like this:
14+
The derived `Add` implementation will allow two structs of the same type to be
15+
added together. This is done by `Add`ing their respective fields together and
16+
creating a new struct with those values.
1717

1818
```rust
1919
# use derive_more::Add;
2020
#
2121
#[derive(Add)]
2222
struct MyInts(i32, i32);
23-
```
24-
25-
Code like this will be generated:
2623

24+
#[derive(Add)]
25+
struct Point2D {
26+
x: i32,
27+
y: i32,
28+
}
29+
```
30+
This generates code equivalent to:
2731
```rust
32+
# use std::ops::Add;
33+
#
2834
# struct MyInts(i32, i32);
29-
impl derive_more::core::ops::Add for MyInts {
30-
type Output = MyInts;
31-
fn add(self, rhs: MyInts) -> MyInts {
32-
MyInts(self.0.add(rhs.0), self.1.add(rhs.1))
35+
#
36+
# struct Point2D {
37+
# x: i32,
38+
# y: i32,
39+
# }
40+
#
41+
impl Add for MyInts {
42+
type Output = Self;
43+
44+
fn add(self, rhs: Self) -> Self::Output {
45+
match (self, rhs) {
46+
(Self(self_0, self_1), Self(rhs_0, rhs_1)) => {
47+
Self(Add::add(self_0, rhs_0), Add::add(self_1, rhs_1))
48+
}
49+
}
3350
}
3451
}
35-
```
3652

37-
The behaviour is similar with more or less fields.
53+
impl Add for Point2D {
54+
type Output = Self;
3855

56+
fn add(self, rhs: Self) -> Self::Output {
57+
match (self, rhs) {
58+
(Self { x: self_0, y: self_1 }, Self { x: rhs_0, y: rhs_1 }) => {
59+
Self { x: Add::add(self_0, rhs_0), y: Add::add(self_1, rhs_1) }
60+
}
61+
}
62+
}
63+
}
64+
```
3965

66+
The behaviour is similar with more or less fields.
4067

4168

42-
## Regular structs
69+
### Ignoring
4370

44-
When deriving `Add` for a regular struct with two fields like this:
71+
Sometimes a struct needs to hold a field (most commonly `PhantomData`) that doesn't
72+
participate in `Add` implementation. Such field could be ignored using the `#[add(skip)]`
73+
attribute.
4574

4675
```rust
76+
# use core::marker::PhantomData;
4777
# use derive_more::Add;
4878
#
4979
#[derive(Add)]
50-
struct Point2D {
51-
x: i32,
52-
y: i32,
53-
}
54-
```
55-
56-
Code like this will be generated:
80+
struct TupleWithZst<T>(i32, #[add(skip)] PhantomData<T>);
5781

58-
```rust
59-
# struct Point2D {
60-
# x: i32,
61-
# y: i32,
62-
# }
63-
impl derive_more::core::ops::Add for Point2D {
64-
type Output = Point2D;
65-
fn add(self, rhs: Point2D) -> Point2D {
66-
Point2D {
67-
x: self.x.add(rhs.x),
68-
y: self.y.add(rhs.y),
69-
}
70-
}
82+
#[derive(Add)]
83+
struct StructWithZst<T> {
84+
x: i32,
85+
#[add(skip)] // or #[add(ignore)]
86+
_marker: PhantomData<T>,
7187
}
7288
```
7389

74-
The behaviour is similar for more or less fields.
75-
7690

7791

7892

7993
## Enums
8094

81-
There's a big difference between the code that is generated for the two struct
82-
types and the one that is generated for enums. The code for enums returns
83-
`Result<EnumType>` instead of an `EnumType` itself. This is because adding an
84-
enum to another enum is only possible if both are the same variant. This makes
85-
the generated code much more complex as well, because this check needs to be
86-
done. For instance when deriving `Add` for an enum like this:
95+
For enums each variant can be `Add`ed in a similar way to another instance of the
96+
same variant. There's one big difference however: it returns a `Result<EnumType>`,
97+
because an error is returned when two different variants are `Add`ed together.
8798

8899
```rust
89100
# use derive_more::Add;
@@ -99,10 +110,10 @@ enum MixedInts {
99110
Unit,
100111
}
101112
```
102-
103-
Code like this will be generated:
104-
113+
This generates code equivalent to:
105114
```rust
115+
# use std::ops::Add;
116+
#
106117
# enum MixedInts {
107118
# SmallInt(i32),
108119
# BigInt(i64),
@@ -112,33 +123,35 @@ Code like this will be generated:
112123
# UnsignedTwo(u32),
113124
# Unit,
114125
# }
115-
impl derive_more::core::ops::Add for MixedInts {
116-
type Output = Result<MixedInts, derive_more::BinaryError>;
117-
fn add(self, rhs: MixedInts) -> Result<MixedInts, derive_more::BinaryError> {
126+
#
127+
impl Add for MixedInts {
128+
type Output = Result<Self, derive_more::BinaryError>;
129+
130+
fn add(self, rhs: Self) -> Self::Output {
118131
match (self, rhs) {
119-
(MixedInts::SmallInt(__l_0), MixedInts::SmallInt(__r_0)) => {
120-
Ok(MixedInts::SmallInt(__l_0.add(__r_0)))
132+
(Self::SmallInt(self_0), Self::SmallInt(rhs_0)) => {
133+
Ok(Self::SmallInt(Add::add(self_0, rhs_0)))
121134
}
122-
(MixedInts::BigInt(__l_0), MixedInts::BigInt(__r_0)) => {
123-
Ok(MixedInts::BigInt(__l_0.add(__r_0)))
135+
(Self::BigInt(self_0), Self::BigInt(rhs_0)) => {
136+
Ok(Self::BigInt(Add::add(self_0, rhs_0)))
124137
}
125-
(MixedInts::TwoSmallInts(__l_0, __l_1), MixedInts::TwoSmallInts(__r_0, __r_1)) => {
126-
Ok(MixedInts::TwoSmallInts(__l_0.add(__r_0), __l_1.add(__r_1)))
138+
(Self::TwoSmallInts(self_0, self_1), Self::TwoSmallInts(rhs_0, rhs_1)) => {
139+
Ok(Self::TwoSmallInts(Add::add(self_0, rhs_0), Add::add(self_1, rhs_1)))
127140
}
128-
(MixedInts::NamedSmallInts { x: __l_0, y: __l_1 },
129-
MixedInts::NamedSmallInts { x: __r_0, y: __r_1 }) => {
130-
Ok(MixedInts::NamedSmallInts {
131-
x: __l_0.add(__r_0),
132-
y: __l_1.add(__r_1),
141+
(Self::NamedSmallInts { x: self_0, y: self_1 },
142+
Self::NamedSmallInts { x: rhs_0, y: rhs_1 }) => {
143+
Ok(Self::NamedSmallInts {
144+
x: Add::add(self_0, rhs_0),
145+
y: Add::add(self_1, rhs_1),
133146
})
134147
}
135-
(MixedInts::UnsignedOne(__l_0), MixedInts::UnsignedOne(__r_0)) => {
136-
Ok(MixedInts::UnsignedOne(__l_0.add(__r_0)))
148+
(Self::UnsignedOne(self_0), Self::UnsignedOne(rhs_0)) => {
149+
Ok(Self::UnsignedOne(Add::add(self_0, rhs_0)))
137150
}
138-
(MixedInts::UnsignedTwo(__l_0), MixedInts::UnsignedTwo(__r_0)) => {
139-
Ok(MixedInts::UnsignedTwo(__l_0.add(__r_0)))
151+
(Self::UnsignedTwo(self_0), Self::UnsignedTwo(rhs_0)) => {
152+
Ok(Self::UnsignedTwo(Add::add(self_0, rhs_0)))
140153
}
141-
(MixedInts::Unit, MixedInts::Unit) => Err(derive_more::BinaryError::Unit(
154+
(Self::Unit, Self::Unit) => Err(derive_more::BinaryError::Unit(
142155
derive_more::UnitError::new("add"),
143156
)),
144157
_ => Err(derive_more::BinaryError::Mismatch(
@@ -149,4 +162,28 @@ impl derive_more::core::ops::Add for MixedInts {
149162
}
150163
```
151164

152-
Also note the Unit type that throws an error when adding it to itself.
165+
Also note the `Unit` variant that throws a `derive_more::UnitError` when `Add`ing it to itself.
166+
167+
168+
### Ignoring
169+
170+
Similarly to structs, enum fields could be ignored using the `#[add(skip)]` attribute.
171+
172+
```rust
173+
# use derive_more::Add;
174+
#
175+
struct I32(i32); // doesn't implement `Add`
176+
177+
#[derive(Add)]
178+
enum MixedInts {
179+
TwoSmallInts(i32, #[add(skip)] I32),
180+
NamedSmallInts {
181+
#[add(skip)] // or #[add(ignore)]
182+
x: I32,
183+
y: i32,
184+
},
185+
}
186+
```
187+
188+
> **NOTE**: Ignoring all the fields of a variant or ignoring the variant itself is not allowed
189+
> (results in a compilation error).

0 commit comments

Comments
 (0)