@@ -273,6 +273,8 @@ impl<T: ArrayElement> Array<T> {
273
273
274
274
/// Clears the array, removing all elements.
275
275
pub fn clear ( & mut self ) {
276
+ self . debug_ensure_mutable ( ) ;
277
+
276
278
// SAFETY: No new values are written to the array, we only remove values from the array.
277
279
unsafe { self . as_inner_mut ( ) } . clear ( ) ;
278
280
}
@@ -283,6 +285,8 @@ impl<T: ArrayElement> Array<T> {
283
285
///
284
286
/// If `index` is out of bounds.
285
287
pub fn set ( & mut self , index : usize , value : impl AsArg < T > ) {
288
+ self . debug_ensure_mutable ( ) ;
289
+
286
290
let ptr_mut = self . ptr_mut ( index) ;
287
291
288
292
meta:: arg_into_ref!( value: T ) ;
@@ -298,6 +302,8 @@ impl<T: ArrayElement> Array<T> {
298
302
#[ doc( alias = "append" ) ]
299
303
#[ doc( alias = "push_back" ) ]
300
304
pub fn push ( & mut self , value : impl AsArg < T > ) {
305
+ self . debug_ensure_mutable ( ) ;
306
+
301
307
meta:: arg_into_ref!( value: T ) ;
302
308
303
309
// SAFETY: The array has type `T` and we're writing a value of type `T` to it.
@@ -310,6 +316,8 @@ impl<T: ArrayElement> Array<T> {
310
316
/// On large arrays, this method is much slower than [`push()`][Self::push], as it will move all the array's elements.
311
317
/// The larger the array, the slower `push_front()` will be.
312
318
pub fn push_front ( & mut self , value : impl AsArg < T > ) {
319
+ self . debug_ensure_mutable ( ) ;
320
+
313
321
meta:: arg_into_ref!( value: T ) ;
314
322
315
323
// SAFETY: The array has type `T` and we're writing a value of type `T` to it.
@@ -322,6 +330,8 @@ impl<T: ArrayElement> Array<T> {
322
330
/// _Godot equivalent: `pop_back`_
323
331
#[ doc( alias = "pop_back" ) ]
324
332
pub fn pop ( & mut self ) -> Option < T > {
333
+ self . debug_ensure_mutable ( ) ;
334
+
325
335
( !self . is_empty ( ) ) . then ( || {
326
336
// SAFETY: We do not write any values to the array, we just remove one.
327
337
let variant = unsafe { self . as_inner_mut ( ) } . pop_back ( ) ;
@@ -334,6 +344,8 @@ impl<T: ArrayElement> Array<T> {
334
344
/// Note: On large arrays, this method is much slower than `pop()` as it will move all the
335
345
/// array's elements. The larger the array, the slower `pop_front()` will be.
336
346
pub fn pop_front ( & mut self ) -> Option < T > {
347
+ self . debug_ensure_mutable ( ) ;
348
+
337
349
( !self . is_empty ( ) ) . then ( || {
338
350
// SAFETY: We do not write any values to the array, we just remove one.
339
351
let variant = unsafe { self . as_inner_mut ( ) } . pop_front ( ) ;
@@ -349,6 +361,8 @@ impl<T: ArrayElement> Array<T> {
349
361
/// # Panics
350
362
/// If `index > len()`.
351
363
pub fn insert ( & mut self , index : usize , value : impl AsArg < T > ) {
364
+ self . debug_ensure_mutable ( ) ;
365
+
352
366
let len = self . len ( ) ;
353
367
assert ! (
354
368
index <= len,
@@ -371,6 +385,8 @@ impl<T: ArrayElement> Array<T> {
371
385
/// If `index` is out of bounds.
372
386
#[ doc( alias = "pop_at" ) ]
373
387
pub fn remove ( & mut self , index : usize ) -> T {
388
+ self . debug_ensure_mutable ( ) ;
389
+
374
390
self . check_bounds ( index) ;
375
391
376
392
// SAFETY: We do not write any values to the array, we just remove one.
@@ -385,6 +401,8 @@ impl<T: ArrayElement> Array<T> {
385
401
/// On large arrays, this method is much slower than [`pop()`][Self::pop], as it will move all the array's
386
402
/// elements after the removed element.
387
403
pub fn erase ( & mut self , value : impl AsArg < T > ) {
404
+ self . debug_ensure_mutable ( ) ;
405
+
388
406
meta:: arg_into_ref!( value: T ) ;
389
407
390
408
// SAFETY: We don't write anything to the array.
@@ -394,6 +412,8 @@ impl<T: ArrayElement> Array<T> {
394
412
/// Assigns the given value to all elements in the array. This can be used together with
395
413
/// `resize` to create an array with a given size and initialized elements.
396
414
pub fn fill ( & mut self , value : impl AsArg < T > ) {
415
+ self . debug_ensure_mutable ( ) ;
416
+
397
417
meta:: arg_into_ref!( value: T ) ;
398
418
399
419
// SAFETY: The array has type `T` and we're writing values of type `T` to it.
@@ -407,6 +427,8 @@ impl<T: ArrayElement> Array<T> {
407
427
///
408
428
/// If you know that the new size is smaller, then consider using [`shrink`](Array::shrink) instead.
409
429
pub fn resize ( & mut self , new_size : usize , value : impl AsArg < T > ) {
430
+ self . debug_ensure_mutable ( ) ;
431
+
410
432
let original_size = self . len ( ) ;
411
433
412
434
// SAFETY: While we do insert `Variant::nil()` if the new size is larger, we then fill it with `value` ensuring that all values in the
@@ -437,6 +459,8 @@ impl<T: ArrayElement> Array<T> {
437
459
/// If you want to increase the size of the array, use [`resize`](Array::resize) instead.
438
460
#[ doc( alias = "resize" ) ]
439
461
pub fn shrink ( & mut self , new_size : usize ) -> bool {
462
+ self . debug_ensure_mutable ( ) ;
463
+
440
464
if new_size >= self . len ( ) {
441
465
return false ;
442
466
}
@@ -449,6 +473,8 @@ impl<T: ArrayElement> Array<T> {
449
473
450
474
/// Appends another array at the end of this array. Equivalent of `append_array` in GDScript.
451
475
pub fn extend_array ( & mut self , other : & Array < T > ) {
476
+ self . debug_ensure_mutable ( ) ;
477
+
452
478
// SAFETY: `append_array` will only read values from `other`, and all types can be converted to `Variant`.
453
479
let other: & VariantArray = unsafe { other. assume_type_ref :: < Variant > ( ) } ;
454
480
@@ -692,6 +718,8 @@ impl<T: ArrayElement> Array<T> {
692
718
693
719
/// Reverses the order of the elements in the array.
694
720
pub fn reverse ( & mut self ) {
721
+ self . debug_ensure_mutable ( ) ;
722
+
695
723
// SAFETY: We do not write any values that don't already exist in the array, so all values have the correct type.
696
724
unsafe { self . as_inner_mut ( ) } . reverse ( ) ;
697
725
}
@@ -705,6 +733,8 @@ impl<T: ArrayElement> Array<T> {
705
733
/// _Godot equivalent: `Array.sort()`_
706
734
#[ doc( alias = "sort" ) ]
707
735
pub fn sort_unstable ( & mut self ) {
736
+ self . debug_ensure_mutable ( ) ;
737
+
708
738
// SAFETY: We do not write any values that don't already exist in the array, so all values have the correct type.
709
739
unsafe { self . as_inner_mut ( ) } . sort ( ) ;
710
740
}
@@ -722,6 +752,8 @@ impl<T: ArrayElement> Array<T> {
722
752
where
723
753
F : FnMut ( & T , & T ) -> cmp:: Ordering ,
724
754
{
755
+ self . debug_ensure_mutable ( ) ;
756
+
725
757
let godot_comparator = |args : & [ & Variant ] | {
726
758
let lhs = T :: from_variant ( args[ 0 ] ) ;
727
759
let rhs = T :: from_variant ( args[ 1 ] ) ;
@@ -749,6 +781,8 @@ impl<T: ArrayElement> Array<T> {
749
781
/// _Godot equivalent: `Array.sort_custom()`_
750
782
#[ doc( alias = "sort_custom" ) ]
751
783
pub fn sort_unstable_custom ( & mut self , func : & Callable ) {
784
+ self . debug_ensure_mutable ( ) ;
785
+
752
786
// SAFETY: We do not write any values that don't already exist in the array, so all values have the correct type.
753
787
unsafe { self . as_inner_mut ( ) } . sort_custom ( func) ;
754
788
}
@@ -757,14 +791,58 @@ impl<T: ArrayElement> Array<T> {
757
791
/// global random number generator common to methods such as `randi`. Call `randomize` to
758
792
/// ensure that a new seed will be used each time if you want non-reproducible shuffling.
759
793
pub fn shuffle ( & mut self ) {
794
+ self . debug_ensure_mutable ( ) ;
795
+
760
796
// SAFETY: We do not write any values that don't already exist in the array, so all values have the correct type.
761
797
unsafe { self . as_inner_mut ( ) } . shuffle ( ) ;
762
798
}
763
799
764
- /// Asserts that the given index refers to an existing element.
800
+ /// Turns the array into a shallow-immutable array.
801
+ ///
802
+ /// Makes the array read-only and returns the original array. The array's elements cannot be overridden with different values, and their
803
+ /// order cannot change. Does not apply to nested elements, such as dictionaries. This operation is irreversible.
804
+ ///
805
+ /// In GDScript, arrays are automatically read-only if declared with the `const` keyword.
806
+ ///
807
+ /// # Semantics and alternatives
808
+ /// You can use this in Rust, but the behavior of mutating methods is only validated in a best-effort manner (more than in GDScript though):
809
+ /// some methods like `set()` panic in Debug mode, when used on a read-only array. There is no guarantee that any attempts to change result
810
+ /// in feedback; some may silently do nothing.
811
+ ///
812
+ /// In Rust, you can use shared references (`&Array<T>`) to prevent mutation. Note however that `Clone` can be used to create another
813
+ /// reference, through which mutation can still occur. For deep-immutable arrays, you'll need to keep your `Array` encapsulated or directly
814
+ /// use Rust data structures.
815
+ ///
816
+ /// _Godot equivalent: `make_read_only`_
817
+ #[ doc( alias = "make_read_only" ) ]
818
+ pub fn into_read_only ( self ) -> Self {
819
+ // SAFETY: Changes a per-array property, no elements.
820
+ unsafe { self . as_inner_mut ( ) } . make_read_only ( ) ;
821
+ self
822
+ }
823
+
824
+ /// Returns true if the array is read-only.
825
+ ///
826
+ /// See [`into_read_only()`][Self::into_read_only].
827
+ /// In GDScript, arrays are automatically read-only if declared with the `const` keyword.
828
+ pub fn is_read_only ( & self ) -> bool {
829
+ self . as_inner ( ) . is_read_only ( )
830
+ }
831
+
832
+ /// Best-effort mutability check.
765
833
///
766
834
/// # Panics
835
+ /// In Debug mode, if the array is marked as read-only.
836
+ fn debug_ensure_mutable ( & self ) {
837
+ debug_assert ! (
838
+ !self . is_read_only( ) ,
839
+ "mutating operation on read-only array"
840
+ ) ;
841
+ }
842
+
843
+ /// Asserts that the given index refers to an existing element.
767
844
///
845
+ /// # Panics
768
846
/// If `index` is out of bounds.
769
847
fn check_bounds ( & self , index : usize ) {
770
848
let len = self . len ( ) ;
@@ -777,7 +855,6 @@ impl<T: ArrayElement> Array<T> {
777
855
/// Returns a pointer to the element at the given index.
778
856
///
779
857
/// # Panics
780
- ///
781
858
/// If `index` is out of bounds.
782
859
fn ptr ( & self , index : usize ) -> sys:: GDExtensionConstVariantPtr {
783
860
let ptr = self . ptr_or_null ( index) ;
0 commit comments