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

Selective AsRef/AsMut impl #298

Merged
merged 59 commits into from Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from 54 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
1bbb5a1
[skip ci] Add type parameters to AsRef/AsMut attribute parsing
MegaBluejay Aug 22, 2023
059bf7b
Add handling for type parameters in AsRef/AsMut macro expansion
MegaBluejay Aug 22, 2023
a541e6d
Fix generated bounds
MegaBluejay Aug 22, 2023
62978f3
Add tests for AsRef/AsMut type arguments
MegaBluejay Aug 22, 2023
d3edb42
Update docs with type parameter details
MegaBluejay Aug 22, 2023
084495f
Improve doc comments
MegaBluejay Aug 22, 2023
844cb92
Restore previous error span on multiple-attribute errors
MegaBluejay Aug 22, 2023
398fbe1
Update compile-fail tests
MegaBluejay Aug 22, 2023
04686cc
Update changelog
MegaBluejay Aug 22, 2023
3a7a13a
Merge branch 'master' into 285-as-ref-restricted-impl
MegaBluejay Aug 22, 2023
acbcabb
Refactor out FieldAttribute::into_args function
MegaBluejay Aug 22, 2023
a9cbe19
Improve naming and docs
MegaBluejay Aug 22, 2023
b255a03
Remove unused parsing helper
MegaBluejay Aug 22, 2023
c8060cb
Fix typo, improve doc comments
MegaBluejay Aug 22, 2023
483f0c2
Corrections
tyranron Aug 28, 2023
7b28df1
Extend tests
tyranron Aug 28, 2023
00227e6
Add test cases covering direct explicit impl
tyranron Aug 28, 2023
1635289
Update comments in tests
MegaBluejay Aug 29, 2023
c2e6dc8
Add note about tuple conversions to docs
MegaBluejay Aug 29, 2023
0adc51c
[skip ci] Add compile_fail tests for case with tuple type in struct a…
MegaBluejay Aug 29, 2023
99e993c
Add specialization machinery
MegaBluejay Aug 30, 2023
f96ac44
Switch to using syn::Type instead of polyfill
MegaBluejay Aug 30, 2023
4714689
Add type search util
MegaBluejay Aug 30, 2023
210e970
Fix sized bounds, improve naming
MegaBluejay Aug 30, 2023
06a425c
Add specialization handling to derive
MegaBluejay Aug 30, 2023
4a26f1a
Fix blanket impl
MegaBluejay Aug 30, 2023
990dee8
Add new test cases
MegaBluejay Aug 30, 2023
787ca1e
Update AsRef and AsMut docs
MegaBluejay Aug 30, 2023
2c8537b
Fix specialization machinery on no_std
MegaBluejay Aug 31, 2023
40e1f74
Fix clippy warning in parsing utils
MegaBluejay Aug 31, 2023
7897d9f
Rework expansion to make algorithm more clear
MegaBluejay Aug 31, 2023
abcf391
Use universal function call syntax in the expansion
MegaBluejay Aug 31, 2023
8ddb21c
Fix trait order
MegaBluejay Aug 31, 2023
ffe12cb
Rename ImplVersion to ImplKind, add docs
MegaBluejay Aug 31, 2023
8a0bf5f
Fix doc comment
MegaBluejay Aug 31, 2023
f027d5d
Improve docs
MegaBluejay Aug 31, 2023
929b0ed
Rename helper type search function
MegaBluejay Aug 31, 2023
cf2e100
Move ImplKind definition inside to_tokens
MegaBluejay Aug 31, 2023
4adb992
Extract merging functions from parse_attrs
MegaBluejay Aug 31, 2023
54b442b
Extend tests
MegaBluejay Aug 31, 2023
7d3567a
Add compile_fail tests for renamed generic case
MegaBluejay Aug 31, 2023
ee7a484
Fix compile_fail stderr
MegaBluejay Aug 31, 2023
abbe52a
Add non-generic field tests
MegaBluejay Aug 31, 2023
b90b268
Use non-macro impls for helper struct
MegaBluejay Aug 31, 2023
7642a66
Add lifetime param tests
MegaBluejay Aug 31, 2023
ccf2c9a
Handle lifetime params
MegaBluejay Aug 31, 2023
3a5f22d
Add const param tests
MegaBluejay Aug 31, 2023
7a1f2ab
Handle const params
MegaBluejay Aug 31, 2023
9ece99a
Remove unnecessary parsing
MegaBluejay Sep 1, 2023
df80bbe
Fix const param handling in type argument positions
MegaBluejay Sep 1, 2023
0d9eeae
Improve helper naming in tests
MegaBluejay Sep 1, 2023
a7e0243
Fix param handling description in docs
MegaBluejay Sep 1, 2023
c592579
Use a bound method instead of a free function for param search
MegaBluejay Sep 1, 2023
7efe422
Remove unnecessary returns
MegaBluejay Sep 1, 2023
0db51e2
Implementation bikeshedding
tyranron Sep 5, 2023
7cc8684
Some tests corrections
tyranron Sep 5, 2023
e4abf7b
Docs bikeshedding
tyranron Sep 5, 2023
b23305a
GitHub, are you OK?
tyranron Sep 5, 2023
40424ce
And now?
tyranron Sep 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -59,6 +59,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
([#279](https://github.com/JelteF/derive_more/pull/279))
- `derive_more::derive` module exporting only macros, without traits.
([#290](https://github.com/JelteF/derive_more/pull/290))
- Add support for selective forwarding to `AsRef`/`AsMut` derives.
([#298](https://github.com/JelteF/derive_more/pull/298))

### Changed

Expand Down
2 changes: 1 addition & 1 deletion impl/Cargo.toml
Expand Up @@ -48,7 +48,7 @@ default = []

add = []
add_assign = []
as_ref = []
as_ref = ["syn/extra-traits", "syn/visit"]
constructor = []
debug = ["syn/extra-traits", "dep:unicode-xid"]
deref = []
Expand Down
126 changes: 117 additions & 9 deletions impl/doc/as_mut.md
Expand Up @@ -31,7 +31,7 @@ impl AsMut<String> for MyWrapper {
}
```

It's also possible to use the `#[as_mut(forward)]` attribute to forward
The `#[as_mut(forward)]` attribute can be used to forward
to the `as_mut` implementation of the field. So here `SingleFieldForward`
implements all `AsMut` for all types that `Vec<i32>` implements `AsMut` for.

Expand All @@ -46,22 +46,96 @@ let mut item = SingleFieldForward(vec![]);
let _: &mut [i32] = (&mut item).as_mut();
```

This generates:
This generates code equivalent to:

```rust
# struct SingleFieldForward(Vec<i32>);
impl<__AsT: ?::core::marker::Sized> ::core::convert::AsMut<__AsT> for SingleFieldForward
impl<T: ?Sized> AsMut<T> for SingleFieldForward
where
Vec<i32>: ::core::convert::AsMut<__AsT>,
Vec<i32>: AsMut<T>,
{
#[inline]
fn as_mut(&mut self) -> &mut __AsT {
<Vec<i32> as ::core::convert::AsMut<__AsT>>::as_mut(&mut self.0)
fn as_mut(&mut self) -> &mut T {
self.0.as_mut()
}
}
```

It's also possible to specify concrete types to derive impls for with `#[as_mut(<types>)]`.

These types can include both the type of the field, and types for which the field type implements `AsMut`.

```rust
# use derive_more::AsMut;
#
#[derive(AsMut)]
#[as_mut(str, [u8], String)]
struct Types(String);

let mut item = Types("test".to_owned());
let _: &mut str = item.as_mut();
let _: &mut [u8] = item.as_mut();
let _: &mut String = item.as_mut();_
```

When either the field type or the type specified to convert into contain generic parameters,
they're compared for string equality, and when there's no match assumed to be different types.

For example

```rust
# use derive_more::AsMut;
#
#[derive(AsMut)]
#[as_mut(i32)]
struct Generic<T>(T);
```

Generates a forwarded impl

```rust
# struct Generic<T>(T);
#
impl<T: AsMut<i32>> AsMut<i32> for Generic<T> {
fn as_mut(&mut self) -> &mut i32 {
self.0.as_mut()
}
}
```

And

```rust
# use derive_more::AsMut;
#
#[derive(AsMut)]
#[as_mut(T)]
struct Generic<T>(T);
```

Generates

```rust
# struct Generic<T>(T);
#
impl<T> AsMut<T> for Generic<T> {
fn as_mut(&mut self) -> &mut T {
&mut self.0
}
}
```

Generating code like this is not supported

```rust
# struct Generic<T>(T);
#
impl AsMut<i32> for Generic<i32> {
fn as_mut(&mut self) -> &mut i32 {
&mut self.0
}
}
```


## Structs with Multiple Fields
Expand All @@ -75,7 +149,7 @@ An implementation will be generated for each indicated field.
#
#[derive(AsMut)]
struct MyWrapper {
#[as_mut]
#[as_mut(str)]
name: String,
#[as_mut]
num: i32,
Expand All @@ -91,9 +165,9 @@ Generates:
# num: i32,
# valid: bool,
# }
impl AsMut<String> for MyWrapper {
impl AsMut<str> for MyWrapper {
fn as_mut(&mut self) -> &mut String {
&mut self.name
self.name.as_mut()
}
}

Expand All @@ -104,6 +178,19 @@ impl AsMut<i32> for MyWrapper {
}
```

Only conversions that use a single field are possible with this derive.
Something like this wouldn't work, due to the nature of the `AsMut` trait:

```rust,compile_fail
# use derive_more::AsMut
#
#[derive(AsMut)]
#[as_mut((str, [u8]))]
struct MyWrapper(String, Vec<u8>)
```

If you need to convert into multiple references, consider using the
[`Into`](crate::Into) derive with `#[into(ref_mut)]`.

### Skipping

Expand Down Expand Up @@ -176,6 +263,27 @@ struct ForwardWithOther {
}
```

Multiple forwarded impls with different concrete types, however, can be used.

```rust
# use derive_more::AsMut;
#
#[derive(AsMut)]
struct Types {
#[as_mut(str)]
str: String,
#[as_mut([u8])]
vec: Vec<u8>,
}

let mut item = Types {
str: "test".to_owned(),
vec: vec![0u8],
};

let _: &mut str = item.as_mut();
let _: &mut [u8] = item.as_mut();
```



Expand Down
129 changes: 119 additions & 10 deletions impl/doc/as_ref.md
Expand Up @@ -31,7 +31,7 @@ impl AsRef<String> for MyWrapper {
}
```

It's also possible to use the `#[as_ref(forward)]` attribute to forward
The `#[as_ref(forward)]` attribute can be used to forward
to the `as_ref` implementation of the field. So here `SingleFieldForward`
implements all `AsRef` for all types that `Vec<i32>` implements `AsRef` for.

Expand All @@ -46,22 +46,96 @@ let item = SingleFieldForward(vec![]);
let _: &[i32] = (&item).as_ref();
```

This generates:
This generates code equivalent to:

```rust
# struct SingleFieldForward(Vec<i32>);
impl<__AsT: ?::core::marker::Sized> ::core::convert::AsRef<__AsT> for SingleFieldForward
impl<T: ?Sized> AsRef<T> for SingleFieldForward
where
Vec<i32>: ::core::convert::AsRef<__AsT>,
Vec<i32>: AsRef<T>,
{
#[inline]
fn as_ref(&self) -> &__AsT {
<Vec<i32> as ::core::convert::AsRef<__AsT>>::as_ref(&self.0)
fn as_ref(&self) -> &T {
self.0.as_ref()
}
}
```

It's also possible to specify concrete types to derive impls for with `#[as_ref(<types>)]`.

These types can include both the type of the field, and types for which the field type implements `AsRef`.

```rust
# use derive_more::AsRef;
#
#[derive(AsRef)]
#[as_ref(str, [u8], String)]
struct Types(String);

let item = Types("test".to_owned());
let _: &str = item.as_ref();
let _: &[u8] = item.as_ref();
let _: &String = item.as_ref();
```

When either the field type or the type specified to convert into contain generic parameters,
they're compared for string equality, and when there's no match assumed to be different types.

For example

```rust
# use derive_more::AsRef;
#
#[derive(AsRef)]
#[as_ref(i32)]
struct Generic<T>(T);
```

Generates a forwarded impl

```rust
# struct Generic<T>(T);
#
impl<T: AsRef<i32>> AsRef<i32> for Generic<T> {
fn as_ref(&self) -> &i32 {
self.0.as_ref()
}
}
```

And

```rust
# use derive_more::AsRef;
#
#[derive(AsRef)]
#[as_ref(T)]
struct Generic<T>(T);
```

Generates

```rust
# struct Generic<T>(T);
#
impl<T> AsRef<T> for Generic<T> {
fn as_ref(&self) -> &T {
&self.0
}
}
```

Generating code like this is not supported

```rust
# struct Generic<T>(T);
#
impl AsRef<i32> for Generic<i32> {
fn as_ref(&self) -> &i32 {
&self.0
}
}
```


## Structs with Multiple Fields
Expand All @@ -75,7 +149,7 @@ An implementation will be generated for each indicated field.
#
#[derive(AsRef)]
struct MyWrapper {
#[as_ref]
#[as_ref(str)]
name: String,
#[as_ref]
num: i32,
Expand All @@ -91,9 +165,9 @@ Generates:
# num: i32,
# valid: bool,
# }
impl AsRef<String> for MyWrapper {
fn as_ref(&self) -> &String {
&self.name
impl AsRef<str> for MyWrapper {
fn as_ref(&self) -> &str {
self.name.as_ref()
}
}

Expand All @@ -104,6 +178,20 @@ impl AsRef<i32> for MyWrapper {
}
```

Only conversions that use a single field are possible with this derive.
Something like this wouldn't work, due to the nature of the `AsRef` trait:

```rust,compile_fail
# use derive_more::AsRef
#
#[derive(AsRef)]
#[as_ref((str, [u8]))]
struct MyWrapper(String, Vec<u8>)
```

If you need to convert into multiple references, consider using the
[`Into`](crate::Into) derive with `#[into(ref)]`.


### Skipping

Expand Down Expand Up @@ -176,6 +264,27 @@ struct ForwardWithOther {
}
```

Multiple forwarded impls with different concrete types, however, can be used.

```rust
# use derive_more::AsRef;
#
#[derive(AsRef)]
struct Types {
#[as_ref(str)]
str: String,
#[as_ref([u8])]
vec: Vec<u8>,
}

let item = Types {
str: "test".to_owned(),
vec: vec![0u8],
};

let _: &str = item.as_ref();
let _: &[u8] = item.as_ref();
```



Expand Down