@@ -78,11 +78,17 @@ impl<T: VariantMetadata> Array<T> {
7878 }
7979
8080 /// Returns the number of elements in the array. Equivalent of `size()` in Godot.
81+ ///
82+ /// Retrieving the size incurs an FFI call. If you know the size hasn't changed, you may consider storing
83+ /// it in a variable. For loops, prefer iterators.
8184 pub fn len ( & self ) -> usize {
8285 to_usize ( self . as_inner ( ) . size ( ) )
8386 }
8487
8588 /// Returns `true` if the array is empty.
89+ ///
90+ /// Checking for emptiness incurs an FFI call. If you know the size hasn't changed, you may consider storing
91+ /// it in a variable. For loops, prefer iterators.
8692 pub fn is_empty ( & self ) -> bool {
8793 self . as_inner ( ) . is_empty ( )
8894 }
@@ -150,10 +156,24 @@ impl<T: VariantMetadata> Array<T> {
150156 ///
151157 /// If `index` is out of bounds.
152158 fn ptr ( & self , index : usize ) -> * const Variant {
153- self . check_bounds ( index) ;
159+ let ptr = self . ptr_or_null ( index) ;
160+ assert ! (
161+ !ptr. is_null( ) ,
162+ "Array index {index} out of bounds (len {len})" ,
163+ len = self . len( ) ,
164+ ) ;
165+ ptr
166+ }
167+
168+ /// Returns a pointer to the element at the given index, or null if out of bounds.
169+ fn ptr_or_null ( & self , index : usize ) -> * const Variant {
170+ // SAFETY: array_operator_index_const returns null for invalid indexes.
171+ let variant_ptr = unsafe {
172+ let index = to_i64 ( index) ;
173+ interface_fn ! ( array_operator_index_const) ( self . sys ( ) , index)
174+ } ;
154175
155- // SAFETY: We just checked that the index is not out of bounds.
156- unsafe { self . ptr_unchecked ( index) }
176+ Variant :: ptr_from_sys ( variant_ptr)
157177 }
158178
159179 /// Returns a mutable pointer to the element at the given index.
@@ -162,29 +182,23 @@ impl<T: VariantMetadata> Array<T> {
162182 ///
163183 /// If `index` is out of bounds.
164184 fn ptr_mut ( & self , index : usize ) -> * mut Variant {
165- self . check_bounds ( index) ;
166-
167- // SAFETY: We just checked that the index is not out of bounds.
168- unsafe { self . ptr_mut_unchecked ( index) }
185+ let ptr = self . ptr_mut_or_null ( index) ;
186+ assert ! (
187+ !ptr. is_null( ) ,
188+ "Array index {index} out of bounds (len {len})" ,
189+ len = self . len( ) ,
190+ ) ;
191+ ptr
169192 }
170193
171- /// Returns a pointer to the element at the given index.
172- ///
173- /// # Safety
174- ///
175- /// Calling this with an out-of-bounds index is undefined behavior.
176- unsafe fn ptr_unchecked ( & self , index : usize ) -> * const Variant {
177- let variant_ptr = interface_fn ! ( array_operator_index_const) ( self . sys ( ) , to_i64 ( index) ) ;
178- Variant :: ptr_from_sys ( variant_ptr)
179- }
194+ /// Returns a pointer to the element at the given index, or null if out of bounds.
195+ fn ptr_mut_or_null ( & self , index : usize ) -> * mut Variant {
196+ // SAFETY: array_operator_index returns null for invalid indexes.
197+ let variant_ptr = unsafe {
198+ let index = to_i64 ( index) ;
199+ interface_fn ! ( array_operator_index) ( self . sys ( ) , index)
200+ } ;
180201
181- /// Returns a mutable pointer to the element at the given index.
182- ///
183- /// # Safety
184- ///
185- /// Calling this with an out-of-bounds index is undefined behavior.
186- unsafe fn ptr_mut_unchecked ( & self , index : usize ) -> * mut Variant {
187- let variant_ptr = interface_fn ! ( array_operator_index) ( self . sys ( ) , to_i64 ( index) ) ;
188202 Variant :: ptr_from_sys_mut ( variant_ptr)
189203 }
190204
@@ -365,6 +379,7 @@ impl<T: VariantMetadata + FromVariant> Array<T> {
365379 ///
366380 /// If `index` is out of bounds.
367381 pub fn get ( & self , index : usize ) -> T {
382+ // Panics on out-of-bounds
368383 let ptr = self . ptr ( index) ;
369384
370385 // SAFETY: `ptr()` just verified that the index is not out of bounds.
@@ -582,17 +597,17 @@ unsafe impl<T: VariantMetadata> GodotFfi for Array<T> {
582597 fn move_return_ptr;
583598 }
584599
585- unsafe fn from_arg_ptr ( ptr : sys:: GDExtensionTypePtr , _call_type : sys:: PtrcallType ) -> Self {
586- let array = Self :: from_sys ( ptr) ;
587- std:: mem:: forget ( array. share ( ) ) ;
588- array
589- }
590-
591600 unsafe fn from_sys_init_default ( init_fn : impl FnOnce ( sys:: GDExtensionTypePtr ) ) -> Self {
592601 let mut result = Self :: default ( ) ;
593602 init_fn ( result. sys_mut ( ) ) ;
594603 result
595604 }
605+
606+ unsafe fn from_arg_ptr ( ptr : sys:: GDExtensionTypePtr , _call_type : sys:: PtrcallType ) -> Self {
607+ let array = Self :: from_sys ( ptr) ;
608+ std:: mem:: forget ( array. share ( ) ) ;
609+ array
610+ }
596611}
597612
598613impl < T : VariantMetadata > fmt:: Debug for Array < T > {
@@ -702,9 +717,11 @@ impl<T: VariantMetadata + ToVariant> From<&[T]> for Array<T> {
702717 return array;
703718 }
704719 array. resize ( len) ;
705- let ptr = array. ptr_mut ( 0 ) ;
720+
721+ let ptr = array. ptr_mut_or_null ( 0 ) ;
706722 for ( i, element) in slice. iter ( ) . enumerate ( ) {
707723 // SAFETY: The array contains exactly `len` elements, stored contiguously in memory.
724+ // Also, the pointer is non-null, as we checked for emptiness above.
708725 unsafe {
709726 * ptr. offset ( to_isize ( i) ) = element. to_variant ( ) ;
710727 }
@@ -767,10 +784,11 @@ impl<'a, T: VariantMetadata + FromVariant> Iterator for Iter<'a, T> {
767784 if self . next_idx < self . array . len ( ) {
768785 let idx = self . next_idx ;
769786 self . next_idx += 1 ;
770- // Using `ptr_unchecked` rather than going through `get()` so we can avoid a second
771- // bounds check.
772- // SAFETY: We just checked that the index is not out of bounds.
773- let variant = unsafe { & * self . array . ptr_unchecked ( idx) } ;
787+
788+ let element_ptr = self . array . ptr_or_null ( idx) ;
789+
790+ // SAFETY: We just checked that the index is not out of bounds, so the pointer won't be null.
791+ let variant = unsafe { & * element_ptr } ;
774792 let element = T :: from_variant ( variant) ;
775793 Some ( element)
776794 } else {
0 commit comments