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)); +} + + ``` diff --git a/src/lib.rs b/src/lib.rs index 51b0948..26c3163 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() { @@ -297,6 +330,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, ), }; 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)); + } }