From ad97466b9f4ba220f34b02c112065bead7ed778c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20G=C3=B6rgens?= Date: Wed, 20 Mar 2024 23:45:51 +0800 Subject: [PATCH] Alternative implementation This alternative implementation uses iterators and vectors, but does not require `Copy`. --- impl/src/add_assign_like.rs | 1 + impl/src/add_helpers.rs | 33 ++++++++++++++------------------ impl/src/add_like.rs | 3 ++- impl/src/into.rs | 38 ++++++++++++++++++------------------- tests/add.rs | 5 +++++ 5 files changed, 41 insertions(+), 39 deletions(-) diff --git a/impl/src/add_assign_like.rs b/impl/src/add_assign_like.rs index ad2be2a6..83cb2c2e 100644 --- a/impl/src/add_assign_like.rs +++ b/impl/src/add_assign_like.rs @@ -32,6 +32,7 @@ pub fn expand(input: &DeriveInput, trait_name: &str) -> TokenStream { impl #impl_generics ::derive_more::#trait_ident for #input_type #ty_generics #where_clause { #[inline] fn #method_ident(&mut self, rhs: #input_type #ty_generics) { + let lhs: &mut Self = self; #( #exprs; )* } } diff --git a/impl/src/add_helpers.rs b/impl/src/add_helpers.rs index 77d4bf1b..39f060e7 100644 --- a/impl/src/add_helpers.rs +++ b/impl/src/add_helpers.rs @@ -1,10 +1,10 @@ -use proc_macro2::{Span, TokenStream}; +use proc_macro2::TokenStream; use quote::quote; use syn::{Field, Ident, Index, Type, TypeArray, TypeTuple}; pub fn tuple_exprs(fields: &[&Field], method_ident: &Ident) -> Vec { let fields: Vec<&Type> = fields.iter().map(|field| &field.ty).collect::>(); - inner_tuple_exprs(0, "e! {}, &fields, method_ident) + inner_tuple_exprs("e! {}, &fields, method_ident) } pub fn struct_exprs(fields: &[&Field], method_ident: &Ident) -> Vec { @@ -13,14 +13,12 @@ pub fn struct_exprs(fields: &[&Field], method_ident: &Ident) -> Vec .map(|field| { // It's safe to unwrap because struct fields always have an identifier let field_path = field.ident.as_ref().unwrap(); - elem_content(0, "e! { .#field_path }, &field.ty, method_ident) + elem_content("e! { .#field_path }, &field.ty, method_ident) }) .collect() } pub fn inner_tuple_exprs( - // `depth` is needed for `index_var` generation for nested arrays - depth: usize, field_path: &TokenStream, fields: &[&Type], method_ident: &Ident, @@ -30,40 +28,37 @@ pub fn inner_tuple_exprs( .enumerate() .map(|(i, ty)| { let i = Index::from(i); - elem_content(depth + 1, "e! { #field_path.#i }, ty, method_ident) + elem_content("e! { #field_path.#i }, ty, method_ident) }) .collect() } pub fn elem_content( - depth: usize, field_path: &TokenStream, ty: &Type, method_ident: &Ident, ) -> TokenStream { match ty { Type::Array(TypeArray { elem, .. }) => { - let index_var = Ident::new(&format!("i{}", depth), Span::call_site()); - let fn_body = elem_content( - depth + 1, - "e! { #field_path[#index_var] }, - elem.as_ref(), - method_ident, - ); + let fn_body = elem_content("e! {}, elem.as_ref(), method_ident); - // generates `core::array::from_fn(|i0| self.x[i0].add(rhs.x[i0]))` - quote! { core::array::from_fn(|#index_var| #fn_body) } + quote! { + lhs #field_path.into_iter().zip(rhs #field_path.into_iter()) + .map(|(lhs, rhs)| #fn_body) + .collect::>() + .try_into() + .unwrap_or_else(|_| unreachable!("Lengths should always match.")) + } } Type::Tuple(TypeTuple { elems, .. }) => { let exprs = inner_tuple_exprs( - depth + 1, field_path, &elems.iter().collect::>(), method_ident, ); quote! { (#(#exprs),*) } } - // generates `self.x.add(rhs.x)` - _ => quote! { self #field_path.#method_ident(rhs #field_path) }, + // generates `lhs.x.add(rhs.x)` + _ => quote! { lhs #field_path.#method_ident(rhs #field_path) }, } } diff --git a/impl/src/add_like.rs b/impl/src/add_like.rs index 573ef1a3..67cb8d00 100644 --- a/impl/src/add_like.rs +++ b/impl/src/add_like.rs @@ -48,6 +48,7 @@ pub fn expand(input: &DeriveInput, trait_name: &str) -> TokenStream { #[inline] fn #method_ident(self, rhs: #input_type #ty_generics) -> #output_type { + let lhs: Self = self; #block } } @@ -149,7 +150,7 @@ fn enum_content( }); } quote! { - match (self, rhs) { + match (lhs, rhs) { #(#matches),* } } diff --git a/impl/src/into.rs b/impl/src/into.rs index f2125fc8..bec5df0e 100644 --- a/impl/src/into.rs +++ b/impl/src/into.rs @@ -237,30 +237,30 @@ impl Parse for FieldAttribute { } } +type Untyped = Either>; +impl From for FieldAttribute { + fn from(v: Untyped) -> Self { + match v { + Untyped::Left(skip) => Self { + skip: Some(skip), + convs: None, + }, + Untyped::Right(c) => Self { + skip: None, + convs: Some(match c { + Either::Left(_empty) => ConversionsAttribute::default(), + Either::Right(convs) => convs, + }), + }, + } + } +} + impl attr::ParseMultiple for FieldAttribute { fn parse_attr_with( attr: &syn::Attribute, parser: &P, ) -> syn::Result { - type Untyped = Either>; - impl From for FieldAttribute { - fn from(v: Untyped) -> Self { - match v { - Untyped::Left(skip) => Self { - skip: Some(skip), - convs: None, - }, - Untyped::Right(c) => Self { - skip: None, - convs: Some(match c { - Either::Left(_empty) => ConversionsAttribute::default(), - Either::Right(convs) => convs, - }), - }, - } - } - } - Untyped::parse_attr_with(attr, parser).map(Self::from) } diff --git a/tests/add.rs b/tests/add.rs index 50d71ca7..4346a740 100644 --- a/tests/add.rs +++ b/tests/add.rs @@ -47,3 +47,8 @@ fn test_sanity() { #[derive(Add)] struct TupleRecursive((i32, u8), [(i32, u8); 10]); + +#[derive(Add)] +pub struct GenericArrayStruct { + pub a: [T; 2], +}