From 08e28dc3e948780462fcce21acddf3f5185bd1f2 Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Wed, 25 Jun 2025 13:15:01 +0800 Subject: [PATCH 01/13] Add support for deriving Deref for enums --- impl/src/deref.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++-- impl/src/utils.rs | 2 +- tests/deref.rs | 40 ++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 3 deletions(-) diff --git a/impl/src/deref.rs b/impl/src/deref.rs index eda19c94..a1f3818d 100644 --- a/impl/src/deref.rs +++ b/impl/src/deref.rs @@ -1,10 +1,65 @@ -use crate::utils::{add_extra_where_clauses, SingleFieldData, State}; +use crate::utils::{ + add_extra_where_clauses, numbered_vars, panic_one_field, SingleFieldData, State, +}; use proc_macro2::TokenStream; use quote::quote; -use syn::{parse::Result, DeriveInput}; +use syn::{parse::Result, Data, DeriveInput}; /// Provides the hook to expand `#[derive(Deref)]` into an implementation of `Deref` pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result { + match input.data { + Data::Struct(_) => expand_struct(input, trait_name), + Data::Enum(_) => expand_enum(input, trait_name), + _ => panic!("Only structs and enums can use derive({trait_name})"), + } +} + +fn expand_enum(input: &DeriveInput, trait_name: &'static str) -> Result { + let state = State::with_field_ignore(input, trait_name, trait_name.to_lowercase())?; + + let trait_path = &state.trait_path; + let enum_name = &input.ident; + let (imp_generics, type_generics, where_clause) = input.generics.split_for_impl(); + + let mut target = None; + let mut match_arms = vec![]; + + for variant_state in state.enabled_variant_data().variant_states.into_iter() { + let data = variant_state.enabled_fields_data(); + if data.fields.len() != 1 { + panic_one_field(variant_state.trait_name, &variant_state.trait_attr); + }; + + let vars = numbered_vars(variant_state.fields.len(), ""); + let matcher = data.matcher(&data.field_indexes, &vars); + + if let None = target { + target = Some(data.field_types[0]); + } + + match_arms.push(matcher); + } + + let target = target.unwrap(); + + Ok(quote! { + #[allow(deprecated)] // omit warnings on deprecated fields/variants + #[allow(unreachable_code)] // omit warnings for `!` and other unreachable types + #[automatically_derived] + impl #imp_generics #trait_path for #enum_name #type_generics #where_clause { + type Target = #target; + + #[inline] + fn deref(&self) -> &Self::Target { + match self { + #(#match_arms)|* => __0 + } + } + } + }) +} + +fn expand_struct(input: &DeriveInput, trait_name: &'static str) -> Result { let state = State::with_field_ignore_and_forward( input, trait_name, diff --git a/impl/src/utils.rs b/impl/src/utils.rs index a5c0b20d..a1d92594 100644 --- a/impl/src/utils.rs +++ b/impl/src/utils.rs @@ -246,7 +246,7 @@ pub fn named_to_vec(fields: &FieldsNamed) -> Vec<&Field> { fields.named.iter().collect() } -fn panic_one_field(trait_name: &str, trait_attr: &str) -> ! { +pub fn panic_one_field(trait_name: &str, trait_attr: &str) -> ! { panic!( "derive({trait_name}) only works when forwarding to a single field. \ Try putting #[{trait_attr}] or #[{trait_attr}(ignore)] on the fields in the struct", diff --git a/tests/deref.rs b/tests/deref.rs index 85d9e958..5b856a3a 100644 --- a/tests/deref.rs +++ b/tests/deref.rs @@ -88,6 +88,46 @@ mod never { } } +#[derive(Deref)] +enum MyEnum1<'a> { + Variant1(&'a [u8]), + Variant2(&'a [u8]), + Variant3(&'a [u8]), +} + +#[derive(Deref)] +enum Compression { + Stored(u32), + Zlib(u32), + LZMA1(u32), +} + +#[test] +fn deref_enum() { + let e = Compression::Stored(5); + assert_eq!(*e, 5); +} + +// #[derive(Deref)] +// enum MyEnum { +// Variant1 { +// #[deref] +// named_field1: u8, +// named_field2: bool, +// }, +// Variant2 { +// #[deref] +// named_field1: u8, +// named_field2: bool, +// }, +// } + +// #[derive(Deref)] +// enum MyEnum { +// Variant1(#[deref] u8, bool), +// Variant2(#[deref] u8, bool), +// } + mod deprecated { use super::*; From 5adc4c1fd2f9f1bc0fef40e518f8070e5951bc97 Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Wed, 25 Jun 2025 14:46:52 +0800 Subject: [PATCH 02/13] Fix explicitly enabling enum variant field --- impl/src/utils.rs | 16 ++++++++++++++++ tests/deref.rs | 40 ++++++++++++++++++++-------------------- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/impl/src/utils.rs b/impl/src/utils.rs index a1d92594..1d38affc 100644 --- a/impl/src/utils.rs +++ b/impl/src/utils.rs @@ -531,6 +531,22 @@ impl<'input> State<'input> { .map(|attrs| get_meta_info(&trait_attr, attrs, &allowed_attr_params.field)) .collect(); let meta_infos = meta_infos?; + + let first_match = meta_infos + .iter() + .find_map(|info| info.enabled.map(|_| info)); + + let default_enabled = if trait_name == "Error" { + true + } else { + first_match.map_or(true, |info| !info.enabled.unwrap()) + }; + + let default_info = FullMetaInfo { + enabled: default_enabled, + ..default_info.clone() + }; + let full_meta_infos: Vec<_> = meta_infos .into_iter() .map(|info| info.into_full(default_info.clone())) diff --git a/tests/deref.rs b/tests/deref.rs index 5b856a3a..7a05c2cb 100644 --- a/tests/deref.rs +++ b/tests/deref.rs @@ -89,7 +89,7 @@ mod never { } #[derive(Deref)] -enum MyEnum1<'a> { +enum MyEnum<'a> { Variant1(&'a [u8]), Variant2(&'a [u8]), Variant3(&'a [u8]), @@ -108,25 +108,25 @@ fn deref_enum() { assert_eq!(*e, 5); } -// #[derive(Deref)] -// enum MyEnum { -// Variant1 { -// #[deref] -// named_field1: u8, -// named_field2: bool, -// }, -// Variant2 { -// #[deref] -// named_field1: u8, -// named_field2: bool, -// }, -// } - -// #[derive(Deref)] -// enum MyEnum { -// Variant1(#[deref] u8, bool), -// Variant2(#[deref] u8, bool), -// } +#[derive(Deref)] +enum MyEnum1 { + Variant1 { + #[deref] + named_field1: u8, + named_field2: bool, + }, + Variant2 { + named_field1: u8, + #[deref(ignore)] + named_field2: bool, + }, +} + +#[derive(Deref)] +enum MyEnum2 { + Variant1(u8, #[deref(ignore)] bool), + Variant2(#[deref] u8, bool), +} mod deprecated { use super::*; From 4a4180b07a65285dbfbd3c79a06cfe57f4782328 Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Wed, 25 Jun 2025 15:47:14 +0800 Subject: [PATCH 03/13] Add support for deriving DerefMut for enums --- impl/src/deref_mut.rs | 53 +++++++++++++++++++++++++++++++++++++++++-- tests/deref_mut.rs | 19 ++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/impl/src/deref_mut.rs b/impl/src/deref_mut.rs index 5c9ffe92..c17caaf4 100644 --- a/impl/src/deref_mut.rs +++ b/impl/src/deref_mut.rs @@ -1,10 +1,59 @@ -use crate::utils::{add_extra_where_clauses, SingleFieldData, State}; +use crate::utils::{ + add_extra_where_clauses, numbered_vars, panic_one_field, SingleFieldData, State, +}; use proc_macro2::TokenStream; use quote::quote; -use syn::{parse::Result, DeriveInput}; +use syn::{parse::Result, Data, DeriveInput}; /// Provides the hook to expand `#[derive(DerefMut)]` into an implementation of `DerefMut` pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result { + match input.data { + Data::Struct(_) => expand_struct(input, trait_name), + Data::Enum(_) => expand_enum(input, trait_name), + _ => panic!("Only structs and enums can use derive({trait_name})"), + } +} + +fn expand_enum(input: &DeriveInput, trait_name: &'static str) -> Result { + let state = State::with_field_ignore(input, trait_name, "deref_mut".into())?; + + let trait_path = &state.trait_path; + let enum_name = &input.ident; + let (imp_generics, type_generics, where_clause) = input.generics.split_for_impl(); + + let mut match_arms = vec![]; + + for variant_state in state.enabled_variant_data().variant_states.into_iter() { + let data = variant_state.enabled_fields_data(); + if data.fields.len() != 1 { + panic_one_field(variant_state.trait_name, &variant_state.trait_attr); + }; + + let vars = numbered_vars(variant_state.fields.len(), ""); + let matcher = data.matcher(&data.field_indexes, &vars); + + match_arms.push(matcher); + } + + Ok(quote! { + #[allow(deprecated)] // omit warnings on deprecated fields/variants + #[allow(unreachable_code)] // omit warnings for `!` and other unreachable types + #[automatically_derived] + impl #imp_generics #trait_path for #enum_name #type_generics #where_clause { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + #(#match_arms)|* => __0 + } + } + } + }) +} + +pub fn expand_struct( + input: &DeriveInput, + trait_name: &'static str, +) -> Result { let state = State::with_field_ignore_and_forward(input, trait_name, "deref_mut".into())?; let SingleFieldData { diff --git a/tests/deref_mut.rs b/tests/deref_mut.rs index 349f816e..b5854f20 100644 --- a/tests/deref_mut.rs +++ b/tests/deref_mut.rs @@ -137,6 +137,25 @@ fn deref_mut_generic_forward() { assert_eq!(*boxed, 3i32); } +#[derive(DerefMut)] +enum Compression { + Stored(u32), + Zlib(u32), + LZMA1(u32), +} + +impl ::core::ops::Deref for Compression { + type Target = u32; + + fn deref(&self) -> &Self::Target { + match self { + Compression::Stored(size) => size, + Compression::Zlib(size) => size, + Compression::LZMA1(size) => size, + } + } +} + #[cfg(nightly)] mod never { use super::*; From 7a8a8bf13beb113b8009fcaffe20c877cc8fb46c Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Wed, 25 Jun 2025 17:37:47 +0800 Subject: [PATCH 04/13] Add support for #[deref(forward)] on enum variant fields --- impl/src/deref.rs | 40 ++++++++++++++++++++++++++++++++++------ tests/deref.rs | 2 +- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/impl/src/deref.rs b/impl/src/deref.rs index a1f3818d..11fad2d9 100644 --- a/impl/src/deref.rs +++ b/impl/src/deref.rs @@ -15,14 +15,18 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result Result { - let state = State::with_field_ignore(input, trait_name, trait_name.to_lowercase())?; + let state = State::with_field_ignore_and_forward( + input, + trait_name, + trait_name.to_lowercase(), + )?; let trait_path = &state.trait_path; let enum_name = &input.ident; - let (imp_generics, type_generics, where_clause) = input.generics.split_for_impl(); let mut target = None; let mut match_arms = vec![]; + let mut predicates = vec![]; for variant_state in state.enabled_variant_data().variant_states.into_iter() { let data = variant_state.enabled_fields_data(); @@ -33,15 +37,39 @@ fn expand_enum(input: &DeriveInput, trait_name: &'static str) -> Result #var + }); } let target = target.unwrap(); + let generics = if predicates.is_empty() { + &input.generics + } else { + &add_extra_where_clauses(&input.generics, quote! { where #(#predicates),* }) + }; + + let (imp_generics, type_generics, where_clause) = generics.split_for_impl(); + Ok(quote! { #[allow(deprecated)] // omit warnings on deprecated fields/variants #[allow(unreachable_code)] // omit warnings for `!` and other unreachable types @@ -52,7 +80,7 @@ fn expand_enum(input: &DeriveInput, trait_name: &'static str) -> Result &Self::Target { match self { - #(#match_arms)|* => __0 + #(#match_arms),* } } } diff --git a/tests/deref.rs b/tests/deref.rs index 7a05c2cb..940bf554 100644 --- a/tests/deref.rs +++ b/tests/deref.rs @@ -98,7 +98,7 @@ enum MyEnum<'a> { #[derive(Deref)] enum Compression { Stored(u32), - Zlib(u32), + Zlib(#[deref(forward)] Box), LZMA1(u32), } From de464e366b88b3cf2e80a4fc0f5831de0267b000 Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Wed, 25 Jun 2025 17:49:41 +0800 Subject: [PATCH 05/13] Add support for #[deref_mut(forward)] on enum variant fields --- impl/src/deref_mut.rs | 30 ++++++++++++++++++++++++++---- tests/deref_mut.rs | 2 +- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/impl/src/deref_mut.rs b/impl/src/deref_mut.rs index c17caaf4..f43cf882 100644 --- a/impl/src/deref_mut.rs +++ b/impl/src/deref_mut.rs @@ -15,13 +15,14 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result Result { - let state = State::with_field_ignore(input, trait_name, "deref_mut".into())?; + let state = + State::with_field_ignore_and_forward(input, trait_name, "deref_mut".into())?; let trait_path = &state.trait_path; let enum_name = &input.ident; - let (imp_generics, type_generics, where_clause) = input.generics.split_for_impl(); let mut match_arms = vec![]; + let mut predicates = vec![]; for variant_state in state.enabled_variant_data().variant_states.into_iter() { let data = variant_state.enabled_fields_data(); @@ -32,9 +33,30 @@ fn expand_enum(input: &DeriveInput, trait_name: &'static str) -> Result #var + }); } + let generics = if predicates.is_empty() { + &input.generics + } else { + &add_extra_where_clauses(&input.generics, quote! { where #(#predicates),* }) + }; + + let (imp_generics, type_generics, where_clause) = generics.split_for_impl(); + Ok(quote! { #[allow(deprecated)] // omit warnings on deprecated fields/variants #[allow(unreachable_code)] // omit warnings for `!` and other unreachable types @@ -43,7 +65,7 @@ fn expand_enum(input: &DeriveInput, trait_name: &'static str) -> Result &mut Self::Target { match self { - #(#match_arms)|* => __0 + #(#match_arms),* } } } diff --git a/tests/deref_mut.rs b/tests/deref_mut.rs index b5854f20..82b4f970 100644 --- a/tests/deref_mut.rs +++ b/tests/deref_mut.rs @@ -139,8 +139,8 @@ fn deref_mut_generic_forward() { #[derive(DerefMut)] enum Compression { + Zlib(#[deref_mut(forward)] Box), Stored(u32), - Zlib(u32), LZMA1(u32), } From ffafb1ee12e1d2ed6e4b68f4cfc5ab592ecfddc1 Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Wed, 25 Jun 2025 18:37:33 +0800 Subject: [PATCH 06/13] Add more tests for enum Deref --- tests/deref.rs | 103 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 87 insertions(+), 16 deletions(-) diff --git a/tests/deref.rs b/tests/deref.rs index 940bf554..80393ff5 100644 --- a/tests/deref.rs +++ b/tests/deref.rs @@ -96,36 +96,107 @@ enum MyEnum<'a> { } #[derive(Deref)] -enum Compression { - Stored(u32), - Zlib(#[deref(forward)] Box), - LZMA1(u32), +#[deref(forward)] +enum MyBoxedIntEnum { + Variant(Box), } -#[test] -fn deref_enum() { - let e = Compression::Stored(5); - assert_eq!(*e, 5); +#[derive(Deref)] +#[deref(forward)] +enum NumRefEnum<'a> { + Variant1 { + num: &'a i32, + }, + Variant2(&'a i32), + Variant3 { + n: &'a i32, + #[deref(ignore)] + useless: bool, + }, } #[derive(Deref)] -enum MyEnum1 { +enum NumRefEnum2<'a> { Variant1 { #[deref] - named_field1: u8, - named_field2: bool, + num: &'a i32, + useless: bool, + }, + Variant2 { + num: &'a i32, + #[deref(ignore)] + useless: bool, }, + Variant3(&'a i32), +} + +#[derive(Deref)] +enum NumRefEnum3 { + Variant1 { + #[deref(forward)] + num: Box, + useless: bool, + }, + Variant2 { + num: i32, + #[deref(ignore)] + useless: bool, + }, + Variant3(i32), +} + +#[derive(Deref)] +enum NumRefEnum4 { + Variant1(i32), Variant2 { - named_field1: u8, + #[deref(forward)] + num: Box, + useless: bool, + }, + Variant3 { + num: i32, #[deref(ignore)] - named_field2: bool, + useless: bool, }, } #[derive(Deref)] -enum MyEnum2 { - Variant1(u8, #[deref(ignore)] bool), - Variant2(#[deref] u8, bool), +enum GenericBoxEnum1 { + Variant1 { + #[deref(forward)] + b: Box, + }, + Variant2(bool, #[deref(forward)] Box), +} + +#[test] +fn deref_generic_forward_enum_inner() { + let boxed = GenericBoxEnum1::Variant2(true, Box::new(1i32)); + assert_eq!(*boxed, 1i32); +} + +#[derive(Deref)] +enum GenericBoxEnum2 { + Variant1 { + a: T, + #[deref(forward)] + b: Box, + }, + Variant2(bool, #[deref(forward)] Box), +} + +#[derive(Deref)] +#[deref(forward)] +enum GenericBoxEnum3 { + Variant1 { b: Box }, + Variant2(Box), + Variant3(#[deref(ignore)] bool, Box), +} + +#[test] +fn deref_generic_forward_enum_outer() { + let boxed = GenericBoxEnum3::Variant2(Box::new(1i32)); + assert_eq!(*boxed, 1i32); } mod deprecated { From 4376337ba7fa9e0aab3d32d9a7e7f46943337356 Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Wed, 25 Jun 2025 19:17:50 +0800 Subject: [PATCH 07/13] Add more tests for enum DerefMut --- tests/deref_mut.rs | 259 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 250 insertions(+), 9 deletions(-) diff --git a/tests/deref_mut.rs b/tests/deref_mut.rs index 82b4f970..ef339bcf 100644 --- a/tests/deref_mut.rs +++ b/tests/deref_mut.rs @@ -138,24 +138,265 @@ fn deref_mut_generic_forward() { } #[derive(DerefMut)] -enum Compression { - Zlib(#[deref_mut(forward)] Box), - Stored(u32), - LZMA1(u32), +enum MyEnum<'a> { + Variant1(&'a [u8]), + Variant2(&'a [u8]), + Variant3(&'a [u8]), } -impl ::core::ops::Deref for Compression { - type Target = u32; +impl<'a> ::core::ops::Deref for MyEnum<'a> { + type Target = &'a [u8]; fn deref(&self) -> &Self::Target { match self { - Compression::Stored(size) => size, - Compression::Zlib(size) => size, - Compression::LZMA1(size) => size, + MyEnum::Variant1(inner) + | MyEnum::Variant2(inner) + | MyEnum::Variant3(inner) => inner, } } } +#[derive(DerefMut)] +#[deref_mut(forward)] +enum MyBoxedIntEnum { + Variant(Box), +} + +impl ::core::ops::Deref for MyBoxedIntEnum { + type Target = as ::core::ops::Deref>::Target; + #[inline] + fn deref(&self) -> &Self::Target { + match self { + MyBoxedIntEnum::Variant(inner) => { + as ::core::ops::Deref>::deref(inner) + } + } + } +} + +#[derive(DerefMut)] +#[deref_mut(forward)] +enum NumRefEnum<'a> { + Variant1 { + num: &'a i32, + }, + Variant2(&'a i32), + Variant3 { + n: &'a i32, + #[deref_mut(ignore)] + useless: bool, + }, +} + +impl<'a> ::core::ops::Deref for NumRefEnum<'a> { + type Target = <&'a i32 as ::core::ops::Deref>::Target; + #[inline] + fn deref(&self) -> &Self::Target { + match self { + NumRefEnum::Variant1 { num } + | NumRefEnum::Variant2(num) + | NumRefEnum::Variant3 { n: num, useless: _ } => { + <&'a i32 as ::core::ops::Deref>::deref(num) + } + } + } +} + +#[derive(DerefMut)] +enum NumRefEnum2<'a> { + Variant1 { + #[deref_mut] + num: &'a i32, + useless: bool, + }, + Variant2 { + num: &'a i32, + #[deref_mut(ignore)] + useless: bool, + }, + Variant3(&'a i32), +} + +impl<'a> ::core::ops::Deref for NumRefEnum2<'a> { + type Target = &'a i32; + #[inline] + fn deref(&self) -> &Self::Target { + match self { + NumRefEnum2::Variant1 { num, useless: _ } + | NumRefEnum2::Variant2 { num, useless: _ } + | NumRefEnum2::Variant3(num) => num, + } + } +} + +#[derive(DerefMut)] +enum NumRefEnum3 { + Variant1 { + #[deref_mut(forward)] + num: Box, + useless: bool, + }, + Variant2 { + num: i32, + #[deref_mut(ignore)] + useless: bool, + }, + Variant3(i32), +} + +impl ::core::ops::Deref for NumRefEnum3 { + type Target = as ::core::ops::Deref>::Target; + #[inline] + fn deref(&self) -> &Self::Target { + match self { + NumRefEnum3::Variant1 { num, useless: _ } => { + as ::core::ops::Deref>::deref(num) + } + NumRefEnum3::Variant2 { num, useless: _ } | NumRefEnum3::Variant3(num) => { + num + } + } + } +} + +#[derive(DerefMut)] +enum NumRefEnum4 { + Variant1(i32), + Variant2 { + #[deref_mut(forward)] + num: Box, + useless: bool, + }, + Variant3 { + num: i32, + #[deref_mut(ignore)] + useless: bool, + }, +} + +impl ::core::ops::Deref for NumRefEnum4 { + type Target = i32; + #[inline] + fn deref(&self) -> &Self::Target { + match self { + NumRefEnum4::Variant1(num) | NumRefEnum4::Variant3 { num, useless: _ } => { + num + } + NumRefEnum4::Variant2 { num, useless: _ } => { + as ::core::ops::Deref>::deref(num) + } + } + } +} + +#[derive(DerefMut)] +enum GenericVecEnum { + Variant1 { + #[deref_mut] + v: Vec, + useless: bool, + }, + Variant2(#[deref_mut(ignore)] bool, Vec), +} + +impl ::core::ops::Deref for GenericVecEnum { + type Target = Vec; + #[inline] + fn deref(&self) -> &Self::Target { + match self { + GenericVecEnum::Variant1 { v, useless: _ } + | GenericVecEnum::Variant2(_, v) => v, + } + } +} + +#[test] +fn deref_mut_generic_enum() { + let mut gv = GenericVecEnum::Variant2(true, vec![42i32]); + assert!(gv.get_mut(0).is_some()); +} + +#[derive(DerefMut)] +enum GenericBoxEnum1 { + Variant1 { + #[deref_mut(forward)] + b: Box, + useless: bool, + }, + Variant2(bool, #[deref_mut(forward)] Box), +} + +impl ::core::ops::Deref for GenericBoxEnum1 { + type Target = as ::core::ops::Deref>::Target; + #[inline] + fn deref(&self) -> &Self::Target { + match self { + GenericBoxEnum1::Variant1 { b, useless: _ } + | GenericBoxEnum1::Variant2(_, b) => { + as ::core::ops::Deref>::deref(b) + } + } + } +} + +#[test] +fn deref_mut_generic_forward_enum_inner() { + let mut boxed = GenericBoxEnum1::Variant2(true, Box::new(1i32)); + *boxed = 3; + assert_eq!(*boxed, 3i32); +} + +#[derive(DerefMut)] +enum GenericBoxEnum2 { + Variant1 { + a: T, + #[deref_mut(forward)] + b: Box, + }, + Variant2(bool, #[deref_mut(forward)] Box), +} + +impl ::core::ops::Deref for GenericBoxEnum2 { + type Target = as ::core::ops::Deref>::Target; + #[inline] + fn deref(&self) -> &Self::Target { + match self { + GenericBoxEnum2::Variant1 { a: _, b } | GenericBoxEnum2::Variant2(_, b) => { + as ::core::ops::Deref>::deref(b) + } + } + } +} + +#[derive(DerefMut)] +#[deref_mut(forward)] +enum GenericBoxEnum3 { + Variant1 { b: Box }, + Variant2(Box), + Variant3(#[deref_mut(ignore)] bool, Box), +} + +impl ::core::ops::Deref for GenericBoxEnum3 { + type Target = as ::core::ops::Deref>::Target; + #[inline] + fn deref(&self) -> &Self::Target { + match self { + GenericBoxEnum3::Variant1 { b } + | GenericBoxEnum3::Variant2(b) + | GenericBoxEnum3::Variant3(_, b) => { + as ::core::ops::Deref>::deref(b) + } + } + } +} + +#[test] +fn deref_mut_generic_forward_enum_outer() { + let mut boxed = GenericBoxEnum3::Variant2(Box::new(1i32)); + *boxed = 3; + assert_eq!(*boxed, 3i32); +} + #[cfg(nightly)] mod never { use super::*; From 203854fd9d92cc45de03b7944e730e7ecda51acb Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Wed, 25 Jun 2025 20:06:41 +0800 Subject: [PATCH 08/13] Add docs for Deref enum --- impl/doc/deref.md | 93 ++++++++++++++++++++++++++++++++++++++++++++++- tests/deref.rs | 19 +++++++++- 2 files changed, 109 insertions(+), 3 deletions(-) diff --git a/impl/doc/deref.md b/impl/doc/deref.md index c7dc1047..5a66c74f 100644 --- a/impl/doc/deref.md +++ b/impl/doc/deref.md @@ -1,6 +1,6 @@ # Using `#[derive(Deref)]` -Deriving `Deref` only works for a single field of a struct. +Deriving `Deref` only works for a single field of a struct, or a single field of each variant of an enum. It's possible to use it in two ways: 1. Dereferencing to the field, i.e. like if your type was a reference type. @@ -22,10 +22,23 @@ struct Num { num: i32, } +#[derive(Deref)] +enum Enum { + V1(i32), + V2 { num: i32 }, +} + #[derive(Deref)] #[deref(forward)] struct MyBoxedInt(Box); +#[derive(Deref)] +#[deref(forward)] +enum MyBoxedIntEnum { + V1(Box), + V2 { num: Box }, +} + // You can specify the field you want to derive `Deref` for. #[derive(Deref)] struct CoolVec { @@ -34,12 +47,25 @@ struct CoolVec { vec: Vec, } +#[derive(Deref)] +enum CoolVecEnum { + V1(Vec), + V2 { cool: bool, #[deref] vec: Vec }, +} + let num = Num{num: 123}; let boxed = MyBoxedInt(Box::new(123)); let cool_vec = CoolVec{cool: true, vec: vec![123]}; assert_eq!(123, *num); assert_eq!(123, *boxed); assert_eq!(vec![123], *cool_vec); + +let num_v2 = Enum::V2{num: 123}; +let boxed_v1 = MyBoxedIntEnum::V1(Box::new(123)); +let cool_vec_v2 = CoolVecEnum::V2{cool: true, vec: vec![123]}; +assert_eq!(123, *num_v2); +assert_eq!(123, *boxed_v1); +assert_eq!(vec![123], *cool_vec_v2); ``` @@ -104,4 +130,67 @@ impl derive_more::core::ops::Deref for MyBoxedInt { ## Enums -Deriving `Deref` is not supported for enums. +When deriving a non-forwarded `Deref` for an enum: + +```rust +# use derive_more::Deref; +# +#[derive(Deref)] +enum CoolVecEnum { + V1(Vec), + V2 { cool: bool, #[deref] vec: Vec }, +} +``` + +Code like this will be generated: + +```rust +# enum CoolVecEnum { +# V1(Vec), +# V2 { cool: bool, vec: Vec }, +# } +impl derive_more::with_trait::Deref for CoolVecEnum { + type Target = Vec; + #[inline] + fn deref(&self) -> &Self::Target { + match self { + CoolVecEnum::V1(__0) => __0, + CoolVecEnum::V2 { cool: _, vec: __0 } => __0, + } + } +} +``` + +When deriving a forwarded `Deref` for an enum: + +```rust +# use derive_more::Deref; +# +#[derive(Deref)] +#[deref(forward)] +enum MyBoxedIntEnum { + V1(Box), + V2 { num: Box }, +} +``` + +Code like this will be generated: + +```rust +# enum MyBoxedIntEnum { +# V1(Box), +# V2 { num: Box }, +# } +impl derive_more::with_trait::Deref for MyBoxedIntEnum { + type Target = as derive_more::with_trait::Deref>::Target; + #[inline] + fn deref(&self) -> &Self::Target { + match self { + MyBoxedIntEnum::V1(__0) => + as derive_more::with_trait::Deref>::deref(__0), + MyBoxedIntEnum::V2 { num: __0 } => + as derive_more::with_trait::Deref>::deref(__0), + } + } +} +``` diff --git a/tests/deref.rs b/tests/deref.rs index 80393ff5..13b60765 100644 --- a/tests/deref.rs +++ b/tests/deref.rs @@ -95,10 +95,27 @@ enum MyEnum<'a> { Variant3(&'a [u8]), } +#[derive(Deref)] +enum Enum { + V1(i32), + V2 { num: i32 }, +} + #[derive(Deref)] #[deref(forward)] enum MyBoxedIntEnum { - Variant(Box), + V1(Box), + V2 { num: Box }, +} + +#[derive(Deref)] +enum CoolVecEnum { + V1(Vec), + V2 { + cool: bool, + #[deref] + vec: Vec, + }, } #[derive(Deref)] From 1ed78fa5841b58517c2e8ac778b739618fa03f8e Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Wed, 25 Jun 2025 20:06:58 +0800 Subject: [PATCH 09/13] Add docs for DerefMut enum --- impl/doc/deref_mut.md | 126 +++++++++++++++++++++++++++++++++++++++++- tests/deref_mut.rs | 43 +++++++++++++- 2 files changed, 164 insertions(+), 5 deletions(-) diff --git a/impl/doc/deref_mut.md b/impl/doc/deref_mut.md index 319a79c8..172bc848 100644 --- a/impl/doc/deref_mut.md +++ b/impl/doc/deref_mut.md @@ -1,6 +1,7 @@ # What `#[derive(DerefMut)]` generates -Deriving `Deref` only works for a single field of a struct. +Deriving `Deref` only works for a single field of a struct, or a single field of +each variant of an enum. Furthermore it requires that the type also implements `Deref`, so usually `Deref` should also be derived. The resulting implementation of `Deref` will allow you to mutably dereference @@ -26,11 +27,25 @@ struct Num { num: i32, } +#[derive(Deref, DerefMut)] +enum Enum { + V1(i32), + V2 { num: i32 }, +} + #[derive(Deref, DerefMut)] #[deref(forward)] #[deref_mut(forward)] struct MyBoxedInt(Box); +#[derive(Deref, DerefMut)] +#[deref(forward)] +#[deref_mut(forward)] +enum MyBoxedIntEnum { + V1(Box), + V2 { num: Box }, +} + // You can specify the field you want to derive DerefMut for #[derive(Deref, DerefMut)] struct CoolVec { @@ -40,6 +55,17 @@ struct CoolVec { vec: Vec, } +#[derive(Deref, DerefMut)] +enum CoolVecEnum { + V1(Vec), + V2 { + cool: bool, + #[deref] + #[deref_mut] + vec: Vec, + }, +} + let mut num = Num{num: 123}; let mut boxed = MyBoxedInt(Box::new(123)); let mut cool_vec = CoolVec{cool: true, vec: vec![123]}; @@ -129,4 +155,100 @@ impl derive_more::core::ops::DerefMut for MyBoxedInt { ## Enums -Deriving `DerefMut` is not supported for enums. +When deriving a non-forwarded `DerefMut` for an enum: + +```rust +# use derive_more::{Deref, DerefMut}; +# +#[derive(Deref, DerefMut)] +enum CoolVecEnum { + V1(Vec), + V2 { + cool: bool, + #[deref] + #[deref_mut] + vec: Vec, + }, +} +``` + +Code like this will be generated: + +```rust +# use ::core::ops::Deref; +# enum CoolVecEnum { +# V1(Vec), +# V2 { +# cool: bool, +# vec: Vec, +# }, +# } +# impl Deref for CoolVecEnum { +# type Target = Vec; +# #[inline] +# fn deref(&self) -> &Self::Target { +# match self { +# CoolVecEnum::V1(__0) => __0, +# CoolVecEnum::V2 { cool: _, vec: __0 } => __0, +# } +# } +# } +impl derive_more::with_trait::DerefMut for CoolVecEnum { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + CoolVecEnum::V1(__0) => __0, + CoolVecEnum::V2 { cool: _, vec: __0 } => __0, + } + } +} +``` + +When deriving a forwarded `DerefMut` for an enum: + +```rust +# use derive_more::{Deref, DerefMut}; +# +#[derive(Deref, DerefMut)] +#[deref(forward)] +#[deref_mut(forward)] +enum MyBoxedIntEnum { + V1(Box), + V2 { num: Box }, +} +``` + +Code like this will be generated: + +```rust +# use ::core::ops::Deref; +# enum MyBoxedIntEnum { +# V1(Box), +# V2 { num: Box }, +# } +# impl Deref for MyBoxedIntEnum { +# type Target = as derive_more::with_trait::Deref>::Target; +# #[inline] +# fn deref(&self) -> &Self::Target { +# match self { +# MyBoxedIntEnum::V1(__0) => +# as derive_more::with_trait::Deref>::deref(__0), +# MyBoxedIntEnum::V2 { num: __0 } => +# as derive_more::with_trait::Deref>::deref(__0), +# } +# } +# } +impl derive_more::with_trait::DerefMut for MyBoxedIntEnum { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + MyBoxedIntEnum::V1(__0) => { + as derive_more::with_trait::DerefMut>::deref_mut(__0) + } + MyBoxedIntEnum::V2 { num: __0 } => { + as derive_more::with_trait::DerefMut>::deref_mut(__0) + } + } + } +} +``` diff --git a/tests/deref_mut.rs b/tests/deref_mut.rs index ef339bcf..2d505720 100644 --- a/tests/deref_mut.rs +++ b/tests/deref_mut.rs @@ -156,10 +156,27 @@ impl<'a> ::core::ops::Deref for MyEnum<'a> { } } +#[derive(DerefMut)] +enum Enum { + V1(i32), + V2 { num: i32 }, +} + +impl ::core::ops::Deref for Enum { + type Target = i32; + #[inline] + fn deref(&self) -> &Self::Target { + match self { + Enum::V1(num) | Enum::V2 { num } => num, + } + } +} + #[derive(DerefMut)] #[deref_mut(forward)] enum MyBoxedIntEnum { - Variant(Box), + V1(Box), + V2 { num: Box }, } impl ::core::ops::Deref for MyBoxedIntEnum { @@ -167,13 +184,33 @@ impl ::core::ops::Deref for MyBoxedIntEnum { #[inline] fn deref(&self) -> &Self::Target { match self { - MyBoxedIntEnum::Variant(inner) => { - as ::core::ops::Deref>::deref(inner) + MyBoxedIntEnum::V1(num) | MyBoxedIntEnum::V2 { num } => { + as ::core::ops::Deref>::deref(num) } } } } +#[derive(DerefMut)] +enum CoolVecEnum { + V1(Vec), + V2 { + cool: bool, + #[deref_mut] + vec: Vec, + }, +} + +impl ::core::ops::Deref for CoolVecEnum { + type Target = Vec; + #[inline] + fn deref(&self) -> &Self::Target { + match self { + CoolVecEnum::V1(vec) | CoolVecEnum::V2 { cool: _, vec } => vec, + } + } +} + #[derive(DerefMut)] #[deref_mut(forward)] enum NumRefEnum<'a> { From 4d999f6556269179d22680c70a22dc34922028ee Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Wed, 25 Jun 2025 20:07:33 +0800 Subject: [PATCH 10/13] Fix typo --- impl/doc/deref_mut.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impl/doc/deref_mut.md b/impl/doc/deref_mut.md index 172bc848..ca52b191 100644 --- a/impl/doc/deref_mut.md +++ b/impl/doc/deref_mut.md @@ -82,7 +82,7 @@ assert_eq!(vec![123, 456], *cool_vec); ## Structs -When deriving a non-forwarded `Deref` for a struct: +When deriving a non-forwarded `DerefMut` for a struct: ```rust # use derive_more::{Deref, DerefMut}; From 7aa5108072b3bc47f0dafdc85be56efbe83de866 Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Wed, 25 Jun 2025 21:13:40 +0800 Subject: [PATCH 11/13] Add CHANGELOG.md entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a803b6d0..9c77d17f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). [#475](https://github.com/JelteF/derive_more/pull/475)) - Proxy-pass `#[allow]`/`#[expect]` attributes of the type in `Constructor` derive. ([#477](https://github.com/JelteF/derive_more/pull/477)) +- Add `Deref` and `DerefMut` derive for enums. + ([#485](https://github.com/JelteF/derive_more/pull/485)) ### Changed From b541dfd9c0547f8d3437c7f3858f26fc9c15745f Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Fri, 27 Jun 2025 01:43:19 +0800 Subject: [PATCH 12/13] Remove unnecessary pub --- impl/src/deref_mut.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/impl/src/deref_mut.rs b/impl/src/deref_mut.rs index f43cf882..48f9feb2 100644 --- a/impl/src/deref_mut.rs +++ b/impl/src/deref_mut.rs @@ -72,10 +72,7 @@ fn expand_enum(input: &DeriveInput, trait_name: &'static str) -> Result Result { +fn expand_struct(input: &DeriveInput, trait_name: &'static str) -> Result { let state = State::with_field_ignore_and_forward(input, trait_name, "deref_mut".into())?; let SingleFieldData { From 4c10faa20e2d2754a86fabe21e0cb9159d465e21 Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 7 Jul 2025 16:25:49 +0200 Subject: [PATCH 13/13] Some corrections --- CHANGELOG.md | 2 +- impl/src/deref.rs | 2 +- impl/src/deref_mut.rs | 2 +- impl/src/utils.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c5d1269..b6c51ef4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ([#479](https://github.com/JelteF/derive_more/pull/479)) - Proxy-pass `#[allow]`/`#[expect]` attributes of the type in `Constructor` derive. ([#477](https://github.com/JelteF/derive_more/pull/477)) -- Add `Deref` and `DerefMut` derive for enums. +- Support `Deref` and `DerefMut` derives for enums. ([#485](https://github.com/JelteF/derive_more/pull/485)) ### Changed diff --git a/impl/src/deref.rs b/impl/src/deref.rs index 11fad2d9..cad120a5 100644 --- a/impl/src/deref.rs +++ b/impl/src/deref.rs @@ -10,7 +10,7 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result expand_struct(input, trait_name), Data::Enum(_) => expand_enum(input, trait_name), - _ => panic!("Only structs and enums can use derive({trait_name})"), + _ => panic!("only structs and enums can use `derive({trait_name})`"), } } diff --git a/impl/src/deref_mut.rs b/impl/src/deref_mut.rs index 48f9feb2..0f991d49 100644 --- a/impl/src/deref_mut.rs +++ b/impl/src/deref_mut.rs @@ -10,7 +10,7 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result expand_struct(input, trait_name), Data::Enum(_) => expand_enum(input, trait_name), - _ => panic!("Only structs and enums can use derive({trait_name})"), + _ => panic!("only structs and enums can use `derive({trait_name})`"), } } diff --git a/impl/src/utils.rs b/impl/src/utils.rs index a66c8275..6689dcac 100644 --- a/impl/src/utils.rs +++ b/impl/src/utils.rs @@ -248,7 +248,7 @@ pub fn named_to_vec(fields: &FieldsNamed) -> Vec<&Field> { fields.named.iter().collect() } -pub fn panic_one_field(trait_name: &str, trait_attr: &str) -> ! { +pub(crate) fn panic_one_field(trait_name: &str, trait_attr: &str) -> ! { panic!( "derive({trait_name}) only works when forwarding to a single field. \ Try putting #[{trait_attr}] or #[{trait_attr}(ignore)] on the fields in the struct",