Skip to content

Commit fdf7fe1

Browse files
ilslvtyranron
andauthored
Redesign Into derive macro (#248)
## Synopsis This PR is a part of replacing all attributes having `syn::Meta` syntax to custom parsing. ## Solution Replace `#[into(types(i32, "&str"))]` with `#[from(i32, &str)]` and add support for deriving multi-field structs and enum variants with `#[from((<tuple>), (<tuple>), ...)]`. Co-authored-by: tyranron <[email protected]>
1 parent 60ed1e7 commit fdf7fe1

25 files changed

+1816
-434
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
3333
practice.
3434
- The `From` derive now uses `#[from(<types>)]` instead of `#[from(types(<types>))]`
3535
and ignores field type itself.
36+
- The `Into` derive now uses `#[into(<types>)]` instead of `#[into(types(<types>))]`
37+
and ignores field type itself.
3638

3739
### Added
3840

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ required-features = ["try_unwrap"]
230230
[[test]]
231231
name = "compile_fail"
232232
path = "tests/compile_fail/mod.rs"
233-
required-features = ["debug", "display", "from"]
233+
required-features = ["debug", "display", "from", "into"]
234234

235235
[[test]]
236236
name = "no_std"

impl/doc/into.md

Lines changed: 70 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,166 +1,119 @@
11
# What `#[derive(Into)]` generates
22

3-
This derive creates the the exact opposite of [`#[derive(From)]`](crate::From).
3+
This derive creates the exact opposite of `#[derive(From)]`.
44
Instead of allowing you to create a new instance of the struct from the values
5-
it should contain, it allows you to extract the values from the struct.
6-
One thing to note is that this derive doesn't actually generate an
7-
implementation for the `Into` trait.
8-
Instead it derives `From` for the values contained in the struct and thus has an
9-
indirect implementation of `Into` as recommended by the
10-
[docs](https://doc.rust-lang.org/core/convert/trait.Into.html).
5+
it should contain, it allows you to extract the values from the struct. One
6+
thing to note is that this derive doesn't actually generate an implementation
7+
for the `Into` trait. Instead, it derives `From` for the values contained in
8+
the struct and thus has an indirect implementation of `Into` as
9+
[recommended by the docs][1].
1110

1211

1312

1413

15-
## Example usage
14+
## Structs
15+
16+
For structs with a single field you can call `.into()` to extract the inner type.
1617

1718
```rust
1819
# use derive_more::Into;
1920
#
20-
// Allow converting into i32
21-
#[derive(Into, PartialEq)]
22-
struct MyInt(i32);
23-
24-
// Additionally convert refs to the inner type refs
25-
#[derive(Into, PartialEq)]
26-
#[into(owned, ref, ref_mut)]
27-
struct MyInt64(i64);
28-
29-
// Specify additional conversions
30-
#[derive(Into, PartialEq)]
31-
#[into(types(i16, i32))]
32-
struct MyInt8(i8);
33-
34-
// Even for ref types
35-
#[derive(Into, PartialEq)]
36-
#[into(owned, ref(types(i64)))]
37-
struct MyInt64Wrapped(MyInt64);
38-
39-
assert!(i32::from(MyInt(2)) == 2i32);
40-
assert!(i64::from(MyInt64(6)) == 6i64);
41-
assert!(<&i64>::from(&MyInt64(6)) == &6i64);
42-
assert!(<&mut i64>::from(&mut MyInt64(6)) == &mut 6i64);
43-
assert!(i8::from(MyInt8(7)) == 7i8);
44-
assert!(i16::from(MyInt8(7)) == 7i16);
45-
assert!(i32::from(MyInt8(7)) == 7i32);
46-
assert!(MyInt64::from(MyInt64Wrapped(MyInt64(1))) == MyInt64(1));
47-
assert!(<&MyInt64>::from(&MyInt64Wrapped(MyInt64(1))) == &MyInt64(1));
48-
assert!(<&i64>::from(&MyInt64Wrapped(MyInt64(1))) == &1i64);
49-
```
50-
51-
52-
21+
#[derive(Debug, Into, PartialEq)]
22+
struct Int(i32);
5323

54-
## Tuple structs
24+
assert_eq!(2, Int(2).into());
25+
```
5526

56-
When deriving `Into` for a tuple struct with a single field (i.e. a newtype) like this:
27+
For structs having multiple fields, `.into()` extracts a tuple containing the
28+
desired content for each field.
5729

5830
```rust
5931
# use derive_more::Into;
6032
#
61-
#[derive(Into)]
62-
struct MyInt(i32);
63-
```
64-
65-
Code like this will be generated:
33+
#[derive(Debug, Into, PartialEq)]
34+
struct Point(i32, i32);
6635

67-
```rust
68-
# struct MyInt(i32);
69-
impl ::core::convert::From<MyInt> for (i32) {
70-
fn from(original: MyInt) -> (i32) {
71-
(original.0)
72-
}
73-
}
36+
assert_eq!((1, 2), Point(1, 2).into());
7437
```
7538

76-
The behaviour is a bit different when deriving for a struct with multiple
77-
fields, since it returns a tuple. For instance when deriving for a tuple struct
78-
with two fields like this:
39+
To specify concrete types for deriving conversions into, use `#[into(<types>)]`.
7940

8041
```rust
42+
# use std::borrow::Cow;
43+
#
8144
# use derive_more::Into;
8245
#
83-
#[derive(Into)]
84-
struct MyInts(i32, i32);
85-
```
46+
#[derive(Debug, Into, PartialEq)]
47+
#[into(Cow<'static, str>, String)]
48+
struct Str(Cow<'static, str>);
8649

87-
Code like this will be generated:
50+
assert_eq!("String".to_owned(), String::from(Str("String".into())));
51+
assert_eq!(Cow::Borrowed("Cow"), <Cow<_>>::from(Str("Cow".into())));
8852

89-
```rust
90-
# struct MyInts(i32, i32);
91-
impl ::core::convert::From<MyInts> for (i32, i32) {
92-
fn from(original: MyInts) -> (i32, i32) {
93-
(original.0, original.1)
94-
}
53+
#[derive(Debug, Into, PartialEq)]
54+
#[into((i64, i64), (i32, i32))]
55+
struct Point {
56+
x: i32,
57+
y: i32,
9558
}
96-
```
97-
98-
9959

60+
assert_eq!((1_i64, 2_i64), Point { x: 1_i32, y: 2_i32 }.into());
61+
assert_eq!((3_i32, 4_i32), Point { x: 3_i32, y: 4_i32 }.into());
62+
```
10063

101-
## Regular structs
102-
103-
For regular structs almost the same code is generated as for tuple structs
104-
except in the way the field values are assigned to the new struct.
105-
When deriving for a regular struct with a single field like this:
64+
In addition to converting to owned types, this macro supports deriving into
65+
reference (mutable or not) via `#[into(ref(...))]`/`#[into(ref_mut(...))]`.
10666

10767
```rust
10868
# use derive_more::Into;
10969
#
110-
#[derive(Into)]
111-
struct Point1D {
112-
x: i32,
113-
}
114-
```
70+
#[derive(Debug, Into, PartialEq)]
71+
#[into(owned, ref(i32), ref_mut)]
72+
struct Int(i32);
11573

116-
Code like this will be generated:
117-
118-
```rust
119-
# struct Point1D {
120-
# x: i32,
121-
# }
122-
impl ::core::convert::From<Point1D> for (i32) {
123-
fn from(original: Point1D) -> (i32) {
124-
(original.x)
125-
}
126-
}
74+
assert_eq!(2, Int(2).into());
75+
assert_eq!(&2, <&i32>::from(&Int(2)));
76+
assert_eq!(&mut 2, <&mut i32>::from(&mut Int(2)));
12777
```
12878

129-
The behaviour is again a bit different when deriving for a struct with multiple
130-
fields, because this also returns a tuple. For instance when deriving for a
131-
tuple struct with two fields like this:
79+
In case there are fields, that shouldn't be included in the conversion, use the
80+
`#[into(skip)]` attribute.
13281

13382
```rust
83+
# use std::marker::PhantomData;
84+
#
13485
# use derive_more::Into;
13586
#
136-
#[derive(Into)]
137-
struct Point2D {
138-
x: i32,
139-
y: i32,
87+
# struct Gram;
88+
#
89+
#[derive(Debug, Into, PartialEq)]
90+
#[into(i32, i64, i128)]
91+
struct Mass<Unit> {
92+
value: i32,
93+
#[into(skip)]
94+
_unit: PhantomData<Unit>,
14095
}
14196

142-
```
143-
144-
Code like this will be generated:
145-
146-
```rust
147-
# struct Point2D {
148-
# x: i32,
149-
# y: i32,
97+
assert_eq!(5, Mass::<Gram>::new(5).into());
98+
assert_eq!(5_i64, Mass::<Gram>::new(5).into());
99+
assert_eq!(5_i128, Mass::<Gram>::new(5).into());
100+
#
101+
# impl<Unit> Mass<Unit> {
102+
# fn new(value: i32) -> Self {
103+
# Self {
104+
# value,
105+
# _unit: PhantomData,
106+
# }
107+
# }
150108
# }
151-
impl ::core::convert::From<Point2D> for (i32, i32) {
152-
fn from(original: Point2D) -> (i32, i32) {
153-
(original.x, original.y)
154-
}
155-
}
156109
```
157110

111+
## Enums
112+
113+
Deriving `Into` for enums is not supported as it would not always be successful,
114+
so `TryInto` should be used instead.
158115

159116

160117

161-
## Enums
162118

163-
Deriving `Into` for enums is not supported as it would not always be successful.
164-
This is what the currently unstable
165-
[`TryInto`](https://doc.rust-lang.org/core/convert/trait.TryInto.html) should be
166-
used for, which is currently not supported by this library.
119+
[1]: https://doc.rust-lang.org/core/convert/trait.Into.html

0 commit comments

Comments
 (0)