From 379b945c93c77399c790ffb619d8de0ddb67a9cd Mon Sep 17 00:00:00 2001 From: Huan Thieu Nguyen Date: Sun, 2 Feb 2025 22:56:32 +0100 Subject: [PATCH 1/5] add unchecked functions --- src/lib.rs | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 51b0948..b4a7ebb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,6 +127,9 @@ fn unnamed_fields_return( (function_name_mut_ref, doc_mut_ref): (&Ident, &str), (function_name_ref, doc_ref): (&Ident, &str), (function_name_val, doc_val): (&Ident, &str), + (function_name_val_unchecked, doc_val_unchecked): (&Ident, &str), + (function_name_ref_unchecked, doc_ref_unchecked): (&Ident, &str), + (function_name_mut_ref_unchecked, doc_mut_ref_unchecked): (&Ident, &str), fields: &syn::FieldsUnnamed, ) -> TokenStream { let (returns_mut_ref, returns_ref, returns_val, matches) = match fields.unnamed.len() { @@ -206,6 +209,33 @@ fn unnamed_fields_return( _ => ::core::result::Result::Err(self) } } + + #[doc = #doc_val_unchecked ] + #[inline] + pub unsafe fn #function_name_val_unchecked(self) -> #returns_val { + match self { + Self::#variant_name(#matches) => #matches, + _ => std::hint::unreachable_unchecked(), + } + } + + #[doc = #doc_ref_unchecked ] + #[inline] + pub unsafe fn #function_name_ref_unchecked(&self) -> #returns_ref { + match self { + Self::#variant_name(#matches) => #matches, + _ => std::hint::unreachable_unchecked(), + } + } + + #[doc = #doc_mut_ref_unchecked ] + #[inline] + pub unsafe fn #function_name_mut_ref_unchecked(&mut self) -> #returns_mut_ref { + match self { + Self::#variant_name(#matches) => #matches, + _ => std::hint::unreachable_unchecked(), + } + } ) } @@ -216,6 +246,9 @@ fn named_fields_return( (function_name_mut_ref, doc_mut_ref): (&Ident, &str), (function_name_ref, doc_ref): (&Ident, &str), (function_name_val, doc_val): (&Ident, &str), + (function_name_val_unchecked, doc_val_unchecked): (&Ident, &str), + (function_name_ref_unchecked, doc_ref_unchecked): (&Ident, &str), + (function_name_mut_ref_unchecked, doc_mut_ref_unchecked): (&Ident, &str), fields: &syn::FieldsNamed, ) -> TokenStream { let (returns_mut_ref, returns_ref, returns_val, matches) = match fields.named.len() { @@ -296,6 +329,33 @@ fn named_fields_return( } _ => ::core::result::Result::Err(self) } + } + + #[doc = #doc_val_unchecked ] + #[inline] + pub unsafe fn #function_name_val_unchecked(self) -> #returns_val { + match self { + Self::#variant_name(#matches) => #matches, + _ => std::hint::unreachable_unchecked(), + } + } + + #[doc = #doc_ref_unchecked ] + #[inline] + pub unsafe fn #function_name_ref_unchecked(&self) -> #returns_ref { + match self { + Self::#variant_name(#matches) => #matches, + _ => std::hint::unreachable_unchecked(), + } + } + + #[doc = #doc_mut_ref_unchecked ] + #[inline] + pub unsafe fn #function_name_mut_ref_unchecked(&mut self) -> #returns_mut_ref { + match self { + Self::#variant_name(#matches) => #matches, + _ => std::hint::unreachable_unchecked(), + } } ) } @@ -352,6 +412,39 @@ fn impl_all_as_fns(ast: &DeriveInput) -> TokenStream { name, variant_name, ); + let function_name_val_unchecked = Ident::new( + &format!("into_{}_unchecked", variant_name).to_snake_case(), + Span::call_site(), + ); + let doc_val_unchecked = format!( + r#"Unchecked return of the inner fields of `{}::{}`. +# Safety +Results in undefined behavior when it is the incorrect variant."#, + name, variant_name + ); + + let function_name_ref_unchecked = Ident::new( + &format!("as_{}_unchecked", variant_name).to_snake_case(), + Span::call_site(), + ); + let doc_ref_unchecked = format!( + r#"Unchecked reference of the inner fields of `{}::{}`. +# Safety +Results in undefined behavior when it is the incorrect variant."#, + name, variant_name + ); + + let function_name_mut_ref_unchecked = Ident::new( + &format!("as_{}_mut_unchecked", variant_name).to_snake_case(), + Span::call_site(), + ); + let doc_mut_ref_unchecked = format!( + r#"Unchecked mutable reference of the inner fields of `{}::{}`. +# Safety +Results in undefined behavior when it is the incorrect variant."#, + name, variant_name + ); + let tokens = match &variant_data.fields { syn::Fields::Unit => unit_fields_return(variant_name, &function_name_is, &doc_is), syn::Fields::Unnamed(unnamed) => unnamed_fields_return( @@ -360,6 +453,9 @@ fn impl_all_as_fns(ast: &DeriveInput) -> TokenStream { (&function_name_mut_ref, &doc_mut_ref), (&function_name_ref, &doc_ref), (&function_name_val, &doc_val), + (&function_name_val_unchecked, &doc_val_unchecked), + (&function_name_ref_unchecked, &doc_ref_unchecked), + (&function_name_mut_ref_unchecked, &doc_mut_ref_unchecked), unnamed, ), syn::Fields::Named(named) => named_fields_return( @@ -368,6 +464,9 @@ fn impl_all_as_fns(ast: &DeriveInput) -> TokenStream { (&function_name_mut_ref, &doc_mut_ref), (&function_name_ref, &doc_ref), (&function_name_val, &doc_val), + (&function_name_val_unchecked, &doc_val_unchecked), + (&function_name_ref_unchecked, &doc_ref_unchecked), + (&function_name_mut_ref_unchecked, &doc_mut_ref_unchecked), named, ), }; From 6de085216033f72b8d1000880a886901d2da990e Mon Sep 17 00:00:00 2001 From: Huan Thieu Nguyen Date: Sun, 2 Feb 2025 23:09:09 +0100 Subject: [PATCH 2/5] move common part into a separate function --- src/lib.rs | 139 ++++++++++++++--------------------------------------- 1 file changed, 36 insertions(+), 103 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b4a7ebb..83eb3b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,18 +121,10 @@ fn unit_fields_return(variant_name: &syn::Ident, function_name: &Ident, doc: &st } /// returns first the types to return, the match names, and then tokens to the field accesses -fn unnamed_fields_return( - variant_name: &syn::Ident, - (function_name_is, doc_is): (&Ident, &str), - (function_name_mut_ref, doc_mut_ref): (&Ident, &str), - (function_name_ref, doc_ref): (&Ident, &str), - (function_name_val, doc_val): (&Ident, &str), - (function_name_val_unchecked, doc_val_unchecked): (&Ident, &str), - (function_name_ref_unchecked, doc_ref_unchecked): (&Ident, &str), - (function_name_mut_ref_unchecked, doc_mut_ref_unchecked): (&Ident, &str), +fn unnamed_fields_return_types( fields: &syn::FieldsUnnamed, -) -> TokenStream { - let (returns_mut_ref, returns_ref, returns_val, matches) = match fields.unnamed.len() { +) -> (TokenStream, TokenStream, TokenStream, TokenStream) { + match fields.unnamed.len() { 1 => { let field = fields.unnamed.first().expect("no fields on type"); @@ -167,91 +159,14 @@ fn unnamed_fields_return( quote!(#matches), ) } - }; - - quote!( - #[doc = #doc_is ] - #[inline] - #[allow(unused_variables)] - pub fn #function_name_is(&self) -> bool { - matches!(self, Self::#variant_name(#matches)) - } - - #[doc = #doc_mut_ref ] - #[inline] - pub fn #function_name_mut_ref(&mut self) -> ::core::option::Option<#returns_mut_ref> { - match self { - Self::#variant_name(#matches) => { - ::core::option::Option::Some((#matches)) - } - _ => ::core::option::Option::None - } - } - - #[doc = #doc_ref ] - #[inline] - pub fn #function_name_ref(&self) -> ::core::option::Option<#returns_ref> { - match self { - Self::#variant_name(#matches) => { - ::core::option::Option::Some((#matches)) - } - _ => ::core::option::Option::None - } - } - - #[doc = #doc_val ] - #[inline] - pub fn #function_name_val(self) -> ::core::result::Result<#returns_val, Self> { - match self { - Self::#variant_name(#matches) => { - ::core::result::Result::Ok((#matches)) - }, - _ => ::core::result::Result::Err(self) - } - } - - #[doc = #doc_val_unchecked ] - #[inline] - pub unsafe fn #function_name_val_unchecked(self) -> #returns_val { - match self { - Self::#variant_name(#matches) => #matches, - _ => std::hint::unreachable_unchecked(), - } - } - - #[doc = #doc_ref_unchecked ] - #[inline] - pub unsafe fn #function_name_ref_unchecked(&self) -> #returns_ref { - match self { - Self::#variant_name(#matches) => #matches, - _ => std::hint::unreachable_unchecked(), - } - } - - #[doc = #doc_mut_ref_unchecked ] - #[inline] - pub unsafe fn #function_name_mut_ref_unchecked(&mut self) -> #returns_mut_ref { - match self { - Self::#variant_name(#matches) => #matches, - _ => std::hint::unreachable_unchecked(), - } - } - ) + } } /// returns first the types to return, the match names, and then tokens to the field accesses -fn named_fields_return( - variant_name: &syn::Ident, - (function_name_is, doc_is): (&Ident, &str), - (function_name_mut_ref, doc_mut_ref): (&Ident, &str), - (function_name_ref, doc_ref): (&Ident, &str), - (function_name_val, doc_val): (&Ident, &str), - (function_name_val_unchecked, doc_val_unchecked): (&Ident, &str), - (function_name_ref_unchecked, doc_ref_unchecked): (&Ident, &str), - (function_name_mut_ref_unchecked, doc_mut_ref_unchecked): (&Ident, &str), +fn named_fields_return_types( fields: &syn::FieldsNamed, -) -> TokenStream { - let (returns_mut_ref, returns_ref, returns_val, matches) = match fields.named.len() { +) -> (TokenStream, TokenStream, TokenStream, TokenStream) { + match fields.named.len() { 1 => { let field = fields.named.first().expect("no fields on type"); let match_name = field.ident.as_ref().expect("expected a named field"); @@ -288,21 +203,39 @@ fn named_fields_return( quote!(#matches), ) } - }; + } +} +/// create the functions for returning fields +fn create_field_fns( + variant_name: &syn::Ident, + (returns_mut_ref, returns_ref, returns_val, matches): ( + TokenStream, + TokenStream, + TokenStream, + TokenStream, + ), + (function_name_is, doc_is): (&Ident, &str), + (function_name_mut_ref, doc_mut_ref): (&Ident, &str), + (function_name_ref, doc_ref): (&Ident, &str), + (function_name_val, doc_val): (&Ident, &str), + (function_name_val_unchecked, doc_val_unchecked): (&Ident, &str), + (function_name_ref_unchecked, doc_ref_unchecked): (&Ident, &str), + (function_name_mut_ref_unchecked, doc_mut_ref_unchecked): (&Ident, &str), +) -> TokenStream { quote!( #[doc = #doc_is ] #[inline] #[allow(unused_variables)] pub fn #function_name_is(&self) -> bool { - matches!(self, Self::#variant_name{ #matches }) + matches!(self, Self::#variant_name(#matches)) } #[doc = #doc_mut_ref ] #[inline] pub fn #function_name_mut_ref(&mut self) -> ::core::option::Option<#returns_mut_ref> { match self { - Self::#variant_name{ #matches } => { + Self::#variant_name(#matches) => { ::core::option::Option::Some((#matches)) } _ => ::core::option::Option::None @@ -313,7 +246,7 @@ fn named_fields_return( #[inline] pub fn #function_name_ref(&self) -> ::core::option::Option<#returns_ref> { match self { - Self::#variant_name{ #matches } => { + Self::#variant_name(#matches) => { ::core::option::Option::Some((#matches)) } _ => ::core::option::Option::None @@ -324,14 +257,14 @@ fn named_fields_return( #[inline] pub fn #function_name_val(self) -> ::core::result::Result<#returns_val, Self> { match self { - Self::#variant_name{ #matches } => { + Self::#variant_name(#matches) => { ::core::result::Result::Ok((#matches)) - } + }, _ => ::core::result::Result::Err(self) } } - #[doc = #doc_val_unchecked ] + #[doc = #doc_val_unchecked ] #[inline] pub unsafe fn #function_name_val_unchecked(self) -> #returns_val { match self { @@ -447,8 +380,9 @@ Results in undefined behavior when it is the incorrect variant."#, let tokens = match &variant_data.fields { syn::Fields::Unit => unit_fields_return(variant_name, &function_name_is, &doc_is), - syn::Fields::Unnamed(unnamed) => unnamed_fields_return( + syn::Fields::Unnamed(unnamed) => create_field_fns( variant_name, + unnamed_fields_return_types(unnamed), (&function_name_is, &doc_is), (&function_name_mut_ref, &doc_mut_ref), (&function_name_ref, &doc_ref), @@ -456,10 +390,10 @@ Results in undefined behavior when it is the incorrect variant."#, (&function_name_val_unchecked, &doc_val_unchecked), (&function_name_ref_unchecked, &doc_ref_unchecked), (&function_name_mut_ref_unchecked, &doc_mut_ref_unchecked), - unnamed, ), - syn::Fields::Named(named) => named_fields_return( + syn::Fields::Named(named) => create_field_fns( variant_name, + named_fields_return_types(named), (&function_name_is, &doc_is), (&function_name_mut_ref, &doc_mut_ref), (&function_name_ref, &doc_ref), @@ -467,7 +401,6 @@ Results in undefined behavior when it is the incorrect variant."#, (&function_name_val_unchecked, &doc_val_unchecked), (&function_name_ref_unchecked, &doc_ref_unchecked), (&function_name_mut_ref_unchecked, &doc_mut_ref_unchecked), - named, ), }; From 20f12c9db035e353f047e4bb157dad96994990a2 Mon Sep 17 00:00:00 2001 From: Huan Thieu Nguyen Date: Sun, 2 Feb 2025 23:52:47 +0100 Subject: [PATCH 3/5] named and unnamed did have difference in implementation --- src/lib.rs | 143 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 105 insertions(+), 38 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 83eb3b6..26c3163 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,10 +121,18 @@ fn unit_fields_return(variant_name: &syn::Ident, function_name: &Ident, doc: &st } /// returns first the types to return, the match names, and then tokens to the field accesses -fn unnamed_fields_return_types( +fn unnamed_fields_return( + variant_name: &syn::Ident, + (function_name_is, doc_is): (&Ident, &str), + (function_name_mut_ref, doc_mut_ref): (&Ident, &str), + (function_name_ref, doc_ref): (&Ident, &str), + (function_name_val, doc_val): (&Ident, &str), + (function_name_val_unchecked, doc_val_unchecked): (&Ident, &str), + (function_name_ref_unchecked, doc_ref_unchecked): (&Ident, &str), + (function_name_mut_ref_unchecked, doc_mut_ref_unchecked): (&Ident, &str), fields: &syn::FieldsUnnamed, -) -> (TokenStream, TokenStream, TokenStream, TokenStream) { - match fields.unnamed.len() { +) -> TokenStream { + let (returns_mut_ref, returns_ref, returns_val, matches) = match fields.unnamed.len() { 1 => { let field = fields.unnamed.first().expect("no fields on type"); @@ -159,14 +167,91 @@ fn unnamed_fields_return_types( quote!(#matches), ) } - } + }; + + quote!( + #[doc = #doc_is ] + #[inline] + #[allow(unused_variables)] + pub fn #function_name_is(&self) -> bool { + matches!(self, Self::#variant_name(#matches)) + } + + #[doc = #doc_mut_ref ] + #[inline] + pub fn #function_name_mut_ref(&mut self) -> ::core::option::Option<#returns_mut_ref> { + match self { + Self::#variant_name(#matches) => { + ::core::option::Option::Some((#matches)) + } + _ => ::core::option::Option::None + } + } + + #[doc = #doc_ref ] + #[inline] + pub fn #function_name_ref(&self) -> ::core::option::Option<#returns_ref> { + match self { + Self::#variant_name(#matches) => { + ::core::option::Option::Some((#matches)) + } + _ => ::core::option::Option::None + } + } + + #[doc = #doc_val ] + #[inline] + pub fn #function_name_val(self) -> ::core::result::Result<#returns_val, Self> { + match self { + Self::#variant_name(#matches) => { + ::core::result::Result::Ok((#matches)) + }, + _ => ::core::result::Result::Err(self) + } + } + + #[doc = #doc_val_unchecked ] + #[inline] + pub unsafe fn #function_name_val_unchecked(self) -> #returns_val { + match self { + Self::#variant_name(#matches) => (#matches), + _ => std::hint::unreachable_unchecked(), + } + } + + #[doc = #doc_ref_unchecked ] + #[inline] + pub unsafe fn #function_name_ref_unchecked(&self) -> #returns_ref { + match self { + Self::#variant_name(#matches) => (#matches), + _ => std::hint::unreachable_unchecked(), + } + } + + #[doc = #doc_mut_ref_unchecked ] + #[inline] + pub unsafe fn #function_name_mut_ref_unchecked(&mut self) -> #returns_mut_ref { + match self { + Self::#variant_name(#matches) => (#matches), + _ => std::hint::unreachable_unchecked(), + } + } + ) } /// returns first the types to return, the match names, and then tokens to the field accesses -fn named_fields_return_types( +fn named_fields_return( + variant_name: &syn::Ident, + (function_name_is, doc_is): (&Ident, &str), + (function_name_mut_ref, doc_mut_ref): (&Ident, &str), + (function_name_ref, doc_ref): (&Ident, &str), + (function_name_val, doc_val): (&Ident, &str), + (function_name_val_unchecked, doc_val_unchecked): (&Ident, &str), + (function_name_ref_unchecked, doc_ref_unchecked): (&Ident, &str), + (function_name_mut_ref_unchecked, doc_mut_ref_unchecked): (&Ident, &str), fields: &syn::FieldsNamed, -) -> (TokenStream, TokenStream, TokenStream, TokenStream) { - match fields.named.len() { +) -> TokenStream { + let (returns_mut_ref, returns_ref, returns_val, matches) = match fields.named.len() { 1 => { let field = fields.named.first().expect("no fields on type"); let match_name = field.ident.as_ref().expect("expected a named field"); @@ -203,39 +288,21 @@ fn named_fields_return_types( quote!(#matches), ) } - } -} + }; -/// create the functions for returning fields -fn create_field_fns( - variant_name: &syn::Ident, - (returns_mut_ref, returns_ref, returns_val, matches): ( - TokenStream, - TokenStream, - TokenStream, - TokenStream, - ), - (function_name_is, doc_is): (&Ident, &str), - (function_name_mut_ref, doc_mut_ref): (&Ident, &str), - (function_name_ref, doc_ref): (&Ident, &str), - (function_name_val, doc_val): (&Ident, &str), - (function_name_val_unchecked, doc_val_unchecked): (&Ident, &str), - (function_name_ref_unchecked, doc_ref_unchecked): (&Ident, &str), - (function_name_mut_ref_unchecked, doc_mut_ref_unchecked): (&Ident, &str), -) -> TokenStream { quote!( #[doc = #doc_is ] #[inline] #[allow(unused_variables)] pub fn #function_name_is(&self) -> bool { - matches!(self, Self::#variant_name(#matches)) + matches!(self, Self::#variant_name{ #matches }) } #[doc = #doc_mut_ref ] #[inline] pub fn #function_name_mut_ref(&mut self) -> ::core::option::Option<#returns_mut_ref> { match self { - Self::#variant_name(#matches) => { + Self::#variant_name{ #matches } => { ::core::option::Option::Some((#matches)) } _ => ::core::option::Option::None @@ -246,7 +313,7 @@ fn create_field_fns( #[inline] pub fn #function_name_ref(&self) -> ::core::option::Option<#returns_ref> { match self { - Self::#variant_name(#matches) => { + Self::#variant_name{ #matches } => { ::core::option::Option::Some((#matches)) } _ => ::core::option::Option::None @@ -257,9 +324,9 @@ fn create_field_fns( #[inline] pub fn #function_name_val(self) -> ::core::result::Result<#returns_val, Self> { match self { - Self::#variant_name(#matches) => { + Self::#variant_name{ #matches } => { ::core::result::Result::Ok((#matches)) - }, + } _ => ::core::result::Result::Err(self) } } @@ -268,7 +335,7 @@ fn create_field_fns( #[inline] pub unsafe fn #function_name_val_unchecked(self) -> #returns_val { match self { - Self::#variant_name(#matches) => #matches, + Self::#variant_name{ #matches } => (#matches), _ => std::hint::unreachable_unchecked(), } } @@ -277,7 +344,7 @@ fn create_field_fns( #[inline] pub unsafe fn #function_name_ref_unchecked(&self) -> #returns_ref { match self { - Self::#variant_name(#matches) => #matches, + Self::#variant_name{ #matches } => (#matches), _ => std::hint::unreachable_unchecked(), } } @@ -286,7 +353,7 @@ fn create_field_fns( #[inline] pub unsafe fn #function_name_mut_ref_unchecked(&mut self) -> #returns_mut_ref { match self { - Self::#variant_name(#matches) => #matches, + Self::#variant_name{ #matches } => (#matches), _ => std::hint::unreachable_unchecked(), } } @@ -380,9 +447,8 @@ Results in undefined behavior when it is the incorrect variant."#, let tokens = match &variant_data.fields { syn::Fields::Unit => unit_fields_return(variant_name, &function_name_is, &doc_is), - syn::Fields::Unnamed(unnamed) => create_field_fns( + syn::Fields::Unnamed(unnamed) => unnamed_fields_return( variant_name, - unnamed_fields_return_types(unnamed), (&function_name_is, &doc_is), (&function_name_mut_ref, &doc_mut_ref), (&function_name_ref, &doc_ref), @@ -390,10 +456,10 @@ Results in undefined behavior when it is the incorrect variant."#, (&function_name_val_unchecked, &doc_val_unchecked), (&function_name_ref_unchecked, &doc_ref_unchecked), (&function_name_mut_ref_unchecked, &doc_mut_ref_unchecked), + unnamed, ), - syn::Fields::Named(named) => create_field_fns( + syn::Fields::Named(named) => named_fields_return( variant_name, - named_fields_return_types(named), (&function_name_is, &doc_is), (&function_name_mut_ref, &doc_mut_ref), (&function_name_ref, &doc_ref), @@ -401,6 +467,7 @@ Results in undefined behavior when it is the incorrect variant."#, (&function_name_val_unchecked, &doc_val_unchecked), (&function_name_ref_unchecked, &doc_ref_unchecked), (&function_name_mut_ref_unchecked, &doc_mut_ref_unchecked), + named, ), }; From ad360d642b3c14dbbe84b04b8184f030a517bb76 Mon Sep 17 00:00:00 2001 From: Huan Thieu Nguyen Date: Mon, 3 Feb 2025 00:08:26 +0100 Subject: [PATCH 4/5] add tests for unchecked functions --- tests/basic.rs | 18 +++++++++++++++--- tests/generics.rs | 5 +++++ tests/named.rs | 22 +++++++++++++++++++++- tests/snake_case.rs | 12 +++++++++++- tests/unnamed.rs | 23 ++++++++++++++++++++++- 5 files changed, 74 insertions(+), 6 deletions(-) diff --git a/tests/basic.rs b/tests/basic.rs index 49e2d35..fd0a7c3 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -36,7 +36,7 @@ fn test_empty() { assert!(empty.is_none()); } -#[derive(Debug, EnumAsInner)] +#[derive(Debug, EnumAsInner, Clone, Copy)] enum EmptyParendsTest { Empty(), } @@ -59,7 +59,7 @@ fn test_empty_parends() { .expect("should have been something and a unit"); } -#[derive(Debug, EnumAsInner)] +#[derive(Debug, EnumAsInner, Clone, Copy)] enum OneTest { One(u32), } @@ -72,9 +72,15 @@ fn test_one() { assert_eq!(*empty.as_one().unwrap(), 1); assert_eq!(*empty.as_one_mut().unwrap(), 1); assert_eq!(empty.into_one().unwrap(), 1); + + unsafe { + assert_eq!(empty.into_one_unchecked(), 1); + assert_eq!(*empty.as_one_unchecked(), 1); + assert_eq!(*empty.as_one_mut_unchecked(), 1); + } } -#[derive(Debug, EnumAsInner)] +#[derive(Debug, EnumAsInner, Clone, Copy)] enum MultiTest { Multi(u32, u32), } @@ -87,4 +93,10 @@ fn test_multi() { assert_eq!(multi.as_multi().unwrap(), (&1_u32, &1_u32)); assert_eq!(multi.as_multi_mut().unwrap(), (&mut 1_u32, &mut 1_u32)); assert_eq!(multi.into_multi().unwrap(), (1_u32, 1_u32)); + + unsafe { + assert_eq!(multi.as_multi_unchecked(), (&1_u32, &1_u32)); + assert_eq!(multi.as_multi_mut_unchecked(), (&mut 1_u32, &mut 1_u32)); + assert_eq!(multi.into_multi_unchecked(), (1_u32, 1_u32)); + } } diff --git a/tests/generics.rs b/tests/generics.rs index f7a515b..0f31bad 100644 --- a/tests/generics.rs +++ b/tests/generics.rs @@ -46,6 +46,11 @@ fn with_generics() { assert_eq!(with_generics.into_a().unwrap(), 100); assert_eq!(*with_generics.as_a().unwrap(), 100); assert_eq!(*with_generics.as_a_mut().unwrap(), 100); + unsafe { + assert_eq!(with_generics.into_a_unchecked(), 100); + assert_eq!(*with_generics.as_a_unchecked(), 100); + assert_eq!(*with_generics.as_a_mut_unchecked(), 100); + } assert!(with_generics.into_b().is_err()); assert!(with_generics.as_b().is_none()); diff --git a/tests/named.rs b/tests/named.rs index d642722..bdf5980 100644 --- a/tests/named.rs +++ b/tests/named.rs @@ -26,7 +26,7 @@ pub mod name_collisions { #[allow(unused_imports)] use name_collisions::*; -#[derive(Debug, EnumAsInner)] +#[derive(Debug, EnumAsInner, Copy, Clone)] enum ManyVariants { One { one: u32 }, Two { one: u32, two: i32 }, @@ -51,6 +51,11 @@ fn test_one_named() { assert_eq!(*many.as_one().unwrap(), 1_u32); assert_eq!(*many.as_one_mut().unwrap(), 1_u32); + + unsafe { + assert_eq!(*many.as_one_unchecked(), 1_u32); + assert_eq!(*many.as_one_mut_unchecked(), 1_u32); + } } #[test] @@ -70,6 +75,12 @@ fn test_two_named() { assert_eq!(many.as_two().unwrap(), (&1_u32, &2_i32)); assert_eq!(many.as_two_mut().unwrap(), (&mut 1_u32, &mut 2_i32)); assert_eq!(many.into_two().unwrap(), (1_u32, 2_i32)); + + unsafe { + assert_eq!(many.as_two_unchecked(), (&1_u32, &2_i32)); + assert_eq!(many.as_two_mut_unchecked(), (&mut 1_u32, &mut 2_i32)); + assert_eq!(many.into_two_unchecked(), (1_u32, 2_i32)); + } } #[test] @@ -96,4 +107,13 @@ fn test_three_named() { (&mut true, &mut 1_u32, &mut 2_i64) ); assert_eq!(many.into_three().unwrap(), (true, 1_u32, 2_i64)); + + unsafe { + assert_eq!(many.as_three_unchecked(), (&true, &1_u32, &2_i64)); + assert_eq!( + many.as_three_mut_unchecked(), + (&mut true, &mut 1_u32, &mut 2_i64) + ); + assert_eq!(many.into_three_unchecked(), (true, 1_u32, 2_i64)); + } } diff --git a/tests/snake_case.rs b/tests/snake_case.rs index 4b7d593..9ef2757 100644 --- a/tests/snake_case.rs +++ b/tests/snake_case.rs @@ -26,7 +26,7 @@ pub mod name_collisions { #[allow(unused_imports)] use name_collisions::*; -#[derive(Debug, EnumAsInner)] +#[derive(Debug, EnumAsInner, Copy, Clone)] #[allow(non_camel_case_types)] #[allow(clippy::upper_case_acronyms)] enum MixedCaseVariants { @@ -54,6 +54,11 @@ fn test_rust_unnamed() { assert_eq!(*mixed.as_rust_is_cool_though().unwrap(), 42); assert_eq!(mixed.into_rust_is_cool_though().unwrap(), 42); + + unsafe { + assert_eq!(*mixed.as_rust_is_cool_though_unchecked(), 42); + assert_eq!(mixed.into_rust_is_cool_though_unchecked(), 42); + } } #[test] @@ -66,4 +71,9 @@ fn test_ymca_named() { assert_eq!(*mixed.as_ymca().unwrap(), (-32_768)); assert_eq!(mixed.into_ymca().unwrap(), (-32_768)); + + unsafe { + assert_eq!(*mixed.as_ymca_unchecked(), (-32_768)); + assert_eq!(mixed.into_ymca_unchecked(), (-32_768)); + } } diff --git a/tests/unnamed.rs b/tests/unnamed.rs index 4bf0ea5..550edf6 100644 --- a/tests/unnamed.rs +++ b/tests/unnamed.rs @@ -26,7 +26,7 @@ pub mod name_collisions { #[allow(unused_imports)] use name_collisions::*; -#[derive(Debug, EnumAsInner)] +#[derive(Debug, EnumAsInner, Clone, Copy)] enum ManyVariants { One(u32), Two(u32, i32), @@ -52,6 +52,12 @@ fn test_one_unnamed() { assert_eq!(*many.as_one().unwrap(), 1_u32); assert_eq!(*many.as_one_mut().unwrap(), 1_u32); assert_eq!(many.into_one().unwrap(), 1_u32); + + unsafe { + assert_eq!(*many.as_one_unchecked(), 1_u32); + assert_eq!(*many.as_one_mut_unchecked(), 1_u32); + assert_eq!(many.into_one_unchecked(), 1_u32); + } } #[test] @@ -73,6 +79,12 @@ fn test_two_unnamed() { assert_eq!(many.as_two().unwrap(), (&1_u32, &2_i32)); assert_eq!(many.as_two_mut().unwrap(), (&mut 1_u32, &mut 2_i32)); assert_eq!(many.into_two().unwrap(), (1_u32, 2_i32)); + + unsafe { + assert_eq!(many.as_two_unchecked(), (&1_u32, &2_i32)); + assert_eq!(many.as_two_mut_unchecked(), (&mut 1_u32, &mut 2_i32)); + assert_eq!(many.into_two_unchecked(), (1_u32, 2_i32)); + } } #[test] @@ -97,4 +109,13 @@ fn test_three_unnamed() { (&mut true, &mut 1_u32, &mut 2_i64) ); assert_eq!(many.into_three().unwrap(), (true, 1_u32, 2_i64)); + + unsafe { + assert_eq!(many.as_three_unchecked(), (&true, &1_u32, &2_i64)); + assert_eq!( + many.as_three_mut_unchecked(), + (&mut true, &mut 1_u32, &mut 2_i64) + ); + assert_eq!(many.into_three_unchecked(), (true, 1_u32, 2_i64)); + } } From 50e0805425b3cf94b6ed5bd0723a4475ed93543d Mon Sep 17 00:00:00 2001 From: Huan Thieu Nguyen Date: Mon, 3 Feb 2025 00:13:48 +0100 Subject: [PATCH 5/5] Add examples of unchecked functions in the README --- README.md | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3ab4eab..ed53b0e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ enum OneEnum { } ``` -where the inner item can be retrieved with the `as_*()`/`as_*_mut()` or with the `into_*()` functions: +where the inner item can be retrieved with the `as_*()`/`as_*_mut()`, `into_*()` or with the `*unchecked` functions: ```rust let one = OneEnum::One(1); @@ -28,9 +28,15 @@ let mut one = OneEnum::One(2); assert_eq!(*one.as_one().unwrap(), 1); assert_eq!(*one.as_one_mut().unwrap(), 1); assert_eq!(one.into_one().unwrap(), 1); + +unsafe { + assert_eq!(*one.as_one_unchecked(), 1); + assert_eq!(*one.as_one_mut_unchecked(), 1); + assert_eq!(one.into_one_unchecked(), 1); +} ``` -where the result is either a reference for inner items or a tuple containing the inner items. +where the result is either a reference for inner items or a tuple containing the inner items. The `*unchecked` functions are unsafe and return either a reference or a tuple containing the inner items. ## Unit case @@ -74,6 +80,13 @@ let mut many = ManyVariants::Three(true, 1, 2); assert_eq!(many.as_three().unwrap(), (&true, &1_u32, &2_i64)); assert_eq!(many.as_three_mut().unwrap(), (&mut true, &mut 1_u32, &mut 2_i64)); assert_eq!(many.into_three().unwrap(), (true, 1_u32, 2_i64)); + +unsafe { + assert_eq!(many.as_three_unchecked(), (&true, &1_u32, &2_i64)); + assert_eq!(many.as_three_mut_unchecked(), (&mut true, &mut 1_u32, &mut 2_i64)); + assert_eq!(many.into_three_unchecked(), (true, 1_u32, 2_i64)); +} + ``` ## Multiple, named field case @@ -99,4 +112,12 @@ let mut many = ManyVariants::Three{ one: true, two: 1, three: 2 }; assert_eq!(many.as_three().unwrap(), (&true, &1_u32, &2_i64)); assert_eq!(many.as_three_mut().unwrap(), (&mut true, &mut 1_u32, &mut 2_i64)); assert_eq!(many.into_three().unwrap(), (true, 1_u32, 2_i64)); + +unsafe { + assert_eq!(many.as_three_unchecked(), (&true, &1_u32, &2_i64)); + assert_eq!(many.as_three_mut_unchecked(), (&mut true, &mut 1_u32, &mut 2_i64)); + assert_eq!(many.into_three_unchecked(), (true, 1_u32, 2_i64)); +} + + ```