@@ -34,6 +34,9 @@ use sys::{ffi_methods, interface_fn, GodotFfi};
3434/// `Array<T>`, where the type `T` must implement `ArrayElement`. Some types like `Array<T>` cannot
3535/// be stored inside arrays, as Godot prevents nesting.
3636///
37+ /// If you plan to use any integer or float types apart from `i64` and `f64`, read
38+ /// [this documentation](../meta/trait.ArrayElement.html#integer-and-float-types).
39+ ///
3740/// # Reference semantics
3841///
3942/// Like in GDScript, `Array` acts as a reference type: multiple `Array` instances may
@@ -716,19 +719,57 @@ impl<T: ArrayElement> Array<T> {
716719 ///
717720 /// Note also that any `GodotType` can be written to a `Variant` array.
718721 ///
719- /// In the current implementation, both cases will produce a panic rather than undefined
720- /// behavior, but this should not be relied upon.
722+ /// In the current implementation, both cases will produce a panic rather than undefined behavior, but this should not be relied upon.
721723 unsafe fn assume_type < U : ArrayElement > ( self ) -> Array < U > {
722- // SAFETY: The memory layout of `Array<T>` does not depend on `T`.
723- unsafe { std:: mem:: transmute ( self ) }
724+ // The memory layout of `Array<T>` does not depend on `T`.
725+ std:: mem:: transmute :: < Array < T > , Array < U > > ( self )
726+ }
727+
728+ /// # Safety
729+ /// See [`assume_type`](Self::assume_type).
730+ unsafe fn assume_type_ref < U : ArrayElement > ( & self ) -> & Array < U > {
731+ // The memory layout of `Array<T>` does not depend on `T`.
732+ std:: mem:: transmute :: < & Array < T > , & Array < U > > ( self )
733+ }
734+
735+ #[ cfg( debug_assertions) ]
736+ pub ( crate ) fn debug_validate_elements ( & self ) -> Result < ( ) , ConvertError > {
737+ // SAFETY: every element is internally represented as Variant.
738+ let canonical_array = unsafe { self . assume_type_ref :: < Variant > ( ) } ;
739+
740+ // If any element is not convertible, this will return an error.
741+ for elem in canonical_array. iter_shared ( ) {
742+ elem. try_to :: < T > ( ) . map_err ( |_err| {
743+ FromGodotError :: BadArrayTypeInt {
744+ expected : self . type_info ( ) ,
745+ value : elem
746+ . try_to :: < i64 > ( )
747+ . expect ( "origin must be i64 compatible; this is a bug" ) ,
748+ }
749+ . into_error ( self . clone ( ) )
750+ } ) ?;
751+ }
752+
753+ Ok ( ( ) )
754+ }
755+
756+ // No-op in Release. Avoids O(n) conversion checks, but still panics on access.
757+ #[ cfg( not( debug_assertions) ) ]
758+ pub ( crate ) fn debug_validate_elements ( & self ) -> Result < ( ) , ConvertError > {
759+ Ok ( ( ) )
724760 }
725761
726762 /// Returns the runtime type info of this array.
727763 fn type_info ( & self ) -> ArrayTypeInfo {
728764 let variant_type = VariantType :: from_sys (
729765 self . as_inner ( ) . get_typed_builtin ( ) as sys:: GDExtensionVariantType
730766 ) ;
731- let class_name = self . as_inner ( ) . get_typed_class_name ( ) ;
767+
768+ let class_name = if variant_type == VariantType :: OBJECT {
769+ Some ( self . as_inner ( ) . get_typed_class_name ( ) )
770+ } else {
771+ None
772+ } ;
732773
733774 ArrayTypeInfo {
734775 variant_type,
@@ -765,12 +806,23 @@ impl<T: ArrayElement> Array<T> {
765806 if type_info. is_typed ( ) {
766807 let script = Variant :: nil ( ) ;
767808
809+ // A bit contrived because empty StringName is lazy-initialized but must also remain valid.
810+ #[ allow( unused_assignments) ]
811+ let mut empty_string_name = None ;
812+ let class_name = if let Some ( class_name) = & type_info. class_name {
813+ class_name. string_sys ( )
814+ } else {
815+ empty_string_name = Some ( StringName :: default ( ) ) ;
816+ // as_ref() crucial here -- otherwise the StringName is dropped.
817+ empty_string_name. as_ref ( ) . unwrap ( ) . string_sys ( )
818+ } ;
819+
768820 // SAFETY: The array is a newly created empty untyped array.
769821 unsafe {
770822 interface_fn ! ( array_set_typed) (
771823 self . sys_mut ( ) ,
772824 type_info. variant_type . sys ( ) ,
773- type_info . class_name . string_sys ( ) ,
825+ class_name, // must be empty if variant_type != OBJECT.
774826 script. var_sys ( ) ,
775827 ) ;
776828 }
@@ -786,6 +838,19 @@ impl<T: ArrayElement> Array<T> {
786838 }
787839}
788840
841+ impl VariantArray {
842+ /// # Safety
843+ /// - Variant must have type `VariantType::ARRAY`.
844+ /// - Subsequent operations on this array must not rely on the type of the array.
845+ pub ( crate ) unsafe fn from_variant_unchecked ( variant : & Variant ) -> Self {
846+ // See also ffi_from_variant().
847+ Self :: new_with_uninit ( |self_ptr| {
848+ let array_from_variant = sys:: builtin_fn!( array_from_variant) ;
849+ array_from_variant ( self_ptr, sys:: SysPtr :: force_mut ( variant. var_sys ( ) ) ) ;
850+ } )
851+ }
852+ }
853+
789854// ----------------------------------------------------------------------------------------------------------------------------------------------
790855// Traits
791856
@@ -838,14 +903,16 @@ impl<T: ArrayElement> ToGodot for Array<T> {
838903
839904impl < T : ArrayElement > FromGodot for Array < T > {
840905 fn try_from_godot ( via : Self :: Via ) -> Result < Self , ConvertError > {
906+ T :: debug_validate_elements ( & via) ?;
841907 Ok ( via)
842908 }
843909}
844910
845911impl < T : ArrayElement > fmt:: Debug for Array < T > {
846912 fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
847913 // Going through `Variant` because there doesn't seem to be a direct way.
848- write ! ( f, "{:?}" , self . to_variant( ) . stringify( ) )
914+ // Reuse Display.
915+ write ! ( f, "{}" , self . to_variant( ) . stringify( ) )
849916 }
850917}
851918
@@ -878,17 +945,21 @@ impl<T: ArrayElement + fmt::Display> fmt::Display for Array<T> {
878945impl < T : ArrayElement > Clone for Array < T > {
879946 fn clone ( & self ) -> Self {
880947 // SAFETY: `self` is a valid array, since we have a reference that keeps it alive.
881- let array = unsafe {
948+ let copy = unsafe {
882949 Self :: new_with_uninit ( |self_ptr| {
883950 let ctor = sys:: builtin_fn!( array_construct_copy) ;
884951 let args = [ self . sys ( ) ] ;
885952 ctor ( self_ptr, args. as_ptr ( ) ) ;
886953 } )
887954 } ;
888955
889- array
890- . with_checked_type ( )
891- . expect ( "copied array should have same type as original array" )
956+ // Double-check copy's runtime type in Debug mode.
957+ if cfg ! ( debug_assertions) {
958+ copy. with_checked_type ( )
959+ . expect ( "copied array should have same type as original array" )
960+ } else {
961+ copy
962+ }
892963 }
893964}
894965
@@ -1000,6 +1071,7 @@ impl<T: ArrayElement> GodotFfiVariant for Array<T> {
10001071 }
10011072
10021073 fn ffi_from_variant ( variant : & Variant ) -> Result < Self , ConvertError > {
1074+ // First check if the variant is an array. The array conversion shouldn't be called otherwise.
10031075 if variant. get_type ( ) != Self :: variant_type ( ) {
10041076 return Err ( FromVariantError :: BadType {
10051077 expected : Self :: variant_type ( ) ,
@@ -1015,6 +1087,7 @@ impl<T: ArrayElement> GodotFfiVariant for Array<T> {
10151087 } )
10161088 } ;
10171089
1090+ // Then, check the runtime type of the array.
10181091 array. with_checked_type ( )
10191092 }
10201093}
0 commit comments