Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redesign Into derive macro #248

Merged
merged 59 commits into from
Jul 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
518c1b1
WIP
ilslv Feb 9, 2023
99cb368
Add tests
ilslv Feb 10, 2023
a062ce1
Add tests
ilslv Feb 10, 2023
7f24761
Docs and clippy
ilslv Feb 10, 2023
845558e
Fix unit tests
ilslv Feb 10, 2023
6211b1a
Docs
ilslv Feb 10, 2023
e5ef86e
Port compile_fail tests from `Display` derive macro
ilslv Feb 10, 2023
8c8f9ca
More compile_fail tests
ilslv Feb 10, 2023
6e82741
CHANGELOG
ilslv Feb 10, 2023
7080be8
Reimplement `core::fmt::DebugTuple` to add support for `finish_non_ex…
ilslv Feb 13, 2023
20a0cb0
Docs fmt corrections
ilslv Feb 13, 2023
8c58553
Minor correction
ilslv Feb 13, 2023
5a8cf3a
Minor correction
ilslv Feb 13, 2023
558c2f0
Minor corrections [skip ci]
tyranron Feb 14, 2023
819454e
Restructure the modules and guard `Debug` under a separate `debug` fe…
ilslv Feb 16, 2023
aa2d332
Implement `syn::Type` parsing
ilslv Feb 17, 2023
20d107e
Simplify `syn::Type` parsing with assumptions
ilslv Feb 17, 2023
ea8e31c
Attributes parsing
ilslv Feb 20, 2023
50135d4
Merge branch 'master' into from-attribute
ilslv Mar 7, 2023
0b6ca7b
WIP
ilslv Mar 7, 2023
ce2cd00
WIP
ilslv Mar 7, 2023
61f1a16
WIP
ilslv Mar 8, 2023
0d489d6
Implementation that covers all existing test cases
ilslv Mar 9, 2023
382f185
Exhaustively cover struct with unit tests
ilslv Mar 9, 2023
eac8e27
Exhaustively cover enum and generics with unit tests
ilslv Mar 9, 2023
1ee8c09
More unit tests for generic structs
ilslv Mar 9, 2023
ef8fa47
More unit tests for generic enums
ilslv Mar 10, 2023
f4f874b
More unit tests for generics
ilslv Mar 10, 2023
7df415e
More unit tests for generics
ilslv Mar 10, 2023
87e1058
More unit tests for generics
ilslv Mar 10, 2023
eaa3596
Add compile fail tests
ilslv Mar 10, 2023
ff47628
Skip variants without attributes if another variant has `#[from]` or …
ilslv Mar 10, 2023
9680277
Docs
ilslv Mar 10, 2023
e5f6540
Docs
ilslv Mar 10, 2023
295eb0d
Docs and corrections
ilslv Mar 10, 2023
60430f7
Merge branch 'master' into from-attribute
ilslv Mar 10, 2023
c5b1f77
Clippy
ilslv Mar 10, 2023
14e8361
Corrections
ilslv Mar 10, 2023
1d74fdf
Corrections and changelog
ilslv Mar 10, 2023
249a92c
Corrections
ilslv Mar 10, 2023
0f295f5
WIP
ilslv Mar 13, 2023
e93c5ed
WIP
ilslv Mar 14, 2023
8e5313f
WIP
ilslv Mar 14, 2023
cf598fa
Enhance legacy attribute syntax error
ilslv Mar 16, 2023
0061e68
Error on mixing regular types with wrapped into `owned`/`ref`/`ref_mut`
ilslv Mar 17, 2023
4994298
Exhaustively cover with unit tests
ilslv Mar 17, 2023
8116c7c
Add unit tests for `#[into(skip)]`, compile fail tests and docs
ilslv Mar 17, 2023
e6065db
Corrections
ilslv Mar 17, 2023
96083d3
Corrections
ilslv Mar 17, 2023
c27c76f
Corrections
ilslv Mar 17, 2023
3a4a893
CHANGELOG
ilslv Mar 17, 2023
2db2f97
Merge branch 'master' into into-attribute
ilslv Jul 14, 2023
5d77999
Corrections
ilslv Jul 14, 2023
efc860b
Corrections
ilslv Jul 14, 2023
7a4de17
Corrections
ilslv Jul 14, 2023
5c1823e
Corrections
ilslv Jul 14, 2023
33767ab
Strip repeated code
tyranron Jul 14, 2023
6898e00
Correct docs
tyranron Jul 14, 2023
08d35f5
Correct tests
tyranron Jul 14, 2023
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 @@ -33,6 +33,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
practice.
- The `From` derive now uses `#[from(<types>)]` instead of `#[from(types(<types>))]`
and ignores field type itself.
- The `Into` derive now uses `#[into(<types>)]` instead of `#[into(types(<types>))]`
and ignores field type itself.

### Added

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ required-features = ["try_unwrap"]
[[test]]
name = "compile_fail"
path = "tests/compile_fail/mod.rs"
required-features = ["debug", "display", "from"]
required-features = ["debug", "display", "from", "into"]

[[test]]
name = "no_std"
Expand Down
187 changes: 70 additions & 117 deletions impl/doc/into.md
Original file line number Diff line number Diff line change
@@ -1,166 +1,119 @@
# What `#[derive(Into)]` generates

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[recommended by the docs][1].
[recommended by the Rust docs for `Into`][1].





## Example usage
## Structs

For structs with a single field you can call `.into()` to extract the inner type.

```rust
# use derive_more::Into;
#
// Allow converting into i32
#[derive(Into, PartialEq)]
struct MyInt(i32);

// Additionally convert refs to the inner type refs
#[derive(Into, PartialEq)]
#[into(owned, ref, ref_mut)]
struct MyInt64(i64);

// Specify additional conversions
#[derive(Into, PartialEq)]
#[into(types(i16, i32))]
struct MyInt8(i8);

// Even for ref types
#[derive(Into, PartialEq)]
#[into(owned, ref(types(i64)))]
struct MyInt64Wrapped(MyInt64);

assert!(i32::from(MyInt(2)) == 2i32);
assert!(i64::from(MyInt64(6)) == 6i64);
assert!(<&i64>::from(&MyInt64(6)) == &6i64);
assert!(<&mut i64>::from(&mut MyInt64(6)) == &mut 6i64);
assert!(i8::from(MyInt8(7)) == 7i8);
assert!(i16::from(MyInt8(7)) == 7i16);
assert!(i32::from(MyInt8(7)) == 7i32);
assert!(MyInt64::from(MyInt64Wrapped(MyInt64(1))) == MyInt64(1));
assert!(<&MyInt64>::from(&MyInt64Wrapped(MyInt64(1))) == &MyInt64(1));
assert!(<&i64>::from(&MyInt64Wrapped(MyInt64(1))) == &1i64);
```



#[derive(Debug, Into, PartialEq)]
struct Int(i32);

## Tuple structs
assert_eq!(2, Int(2).into());
```

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

```rust
# use derive_more::Into;
#
#[derive(Into)]
struct MyInt(i32);
```

Code like this will be generated:
#[derive(Debug, Into, PartialEq)]
struct Point(i32, i32);

```rust
# struct MyInt(i32);
impl ::core::convert::From<MyInt> for (i32) {
fn from(original: MyInt) -> (i32) {
(original.0)
}
}
assert_eq!((1, 2), Point(1, 2).into());
```

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

```rust
# use std::borrow::Cow;
#
# use derive_more::Into;
#
#[derive(Into)]
struct MyInts(i32, i32);
```
#[derive(Debug, Into, PartialEq)]
#[into(Cow<'static, str>, String)]
struct Str(Cow<'static, str>);

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

```rust
# struct MyInts(i32, i32);
impl ::core::convert::From<MyInts> for (i32, i32) {
fn from(original: MyInts) -> (i32, i32) {
(original.0, original.1)
}
#[derive(Debug, Into, PartialEq)]
#[into((i64, i64), (i32, i32))]
struct Point {
x: i32,
y: i32,
}
```



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

## Regular structs

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

```rust
# use derive_more::Into;
#
#[derive(Into)]
struct Point1D {
x: i32,
}
```
#[derive(Debug, Into, PartialEq)]
#[into(owned, ref(i32), ref_mut)]
Copy link
Owner

@JelteF JelteF Mar 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So into(owned) is equivalent to into(types({current_type}))? Do we really need owned then?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a better example would be to use ref(i64)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, actually what's the difference between ref(i64) and simply using &i64?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JelteF here ref/ref_mut control Self type representation, while &i64 controls target type.

ref(i64) means impl Into<&i64> for &MyType, while &i64 means only impl Into<&i64> for MyType.

struct Int(i32);

Code like this will be generated:

```rust
# struct Point1D {
# x: i32,
# }
impl ::core::convert::From<Point1D> for (i32) {
fn from(original: Point1D) -> (i32) {
(original.x)
}
}
assert_eq!(2, Int(2).into());
assert_eq!(&2, <&i32>::from(&Int(2)));
assert_eq!(&mut 2, <&mut i32>::from(&mut Int(2)));
```

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

```rust
# use std::marker::PhantomData;
#
# use derive_more::Into;
#
#[derive(Into)]
struct Point2D {
x: i32,
y: i32,
# struct Gram;
#
#[derive(Debug, Into, PartialEq)]
#[into(i32, i64, i128)]
struct Mass<Unit> {
value: i32,
#[into(skip)]
_unit: PhantomData<Unit>,
}

```

Code like this will be generated:

```rust
# struct Point2D {
# x: i32,
# y: i32,
assert_eq!(5, Mass::<Gram>::new(5).into());
assert_eq!(5_i64, Mass::<Gram>::new(5).into());
assert_eq!(5_i128, Mass::<Gram>::new(5).into());
#
# impl<Unit> Mass<Unit> {
# fn new(value: i32) -> Self {
# Self {
# value,
# _unit: PhantomData,
# }
# }
# }
impl ::core::convert::From<Point2D> for (i32, i32) {
fn from(original: Point2D) -> (i32, i32) {
(original.x, original.y)
}
}
```

## Enums

Deriving `Into` for enums is not supported as it would not always be successful,
so `TryInto` should be used instead.



## Enums

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