@@ -15,6 +15,7 @@ use sys::{ffi_methods, interface_fn, GodotFfi};
15
15
use crate :: builtin:: * ;
16
16
use crate :: meta;
17
17
use crate :: meta:: error:: { ConvertError , FromGodotError , FromVariantError } ;
18
+ use crate :: meta:: signed_range:: SignedRange ;
18
19
use crate :: meta:: {
19
20
element_godot_type_name, element_variant_type, ArrayElement , AsArg , ClassName , ElementType ,
20
21
ExtVariantType , FromGodot , GodotConvert , GodotFfiVariant , GodotType , PropertyHintInfo , RefArg ,
@@ -108,6 +109,35 @@ use crate::registry::property::{BuiltinExport, Export, Var};
108
109
/// // ...and so on.
109
110
/// ```
110
111
///
112
+ /// # Working with signed ranges and steps
113
+ ///
114
+ /// For negative indices, use [`wrapped()`](crate::meta::wrapped).
115
+ ///
116
+ /// ```no_run
117
+ /// # use godot::builtin::array;
118
+ /// # use godot::meta::wrapped;
119
+ /// let arr = array![0, 1, 2, 3, 4, 5];
120
+ ///
121
+ /// // The values of `begin` (inclusive) and `end` (exclusive) will be clamped to the array size.
122
+ /// let clamped_array = arr.subarray_deep(999..99999, None);
123
+ /// assert_eq!(clamped_array, array![]);
124
+ ///
125
+ /// // If either `begin` or `end` is negative, its value is relative to the end of the array.
126
+ /// let sub = arr.subarray_shallow(wrapped(-1..-5), None);
127
+ /// assert_eq!(sub, array![5, 3]);
128
+ ///
129
+ /// // If `end` is not specified, the range spans through whole array.
130
+ /// let sub = arr.subarray_deep(1.., None);
131
+ /// assert_eq!(sub, array![1, 2, 3, 4, 5]);
132
+ /// let other_clamped_array = arr.subarray_shallow(5.., Some(2));
133
+ /// assert_eq!(other_clamped_array, array![5]);
134
+ ///
135
+ /// // If specified, `step` is the relative index between source elements. It can be negative,
136
+ /// // in which case `begin` must be higher than `end`.
137
+ /// let sub = arr.subarray_shallow(wrapped(-1..-5), Some(-2));
138
+ /// assert_eq!(sub, array![5, 3]);
139
+ /// ```
140
+ ///
111
141
/// # Thread safety
112
142
///
113
143
/// Usage is safe if the `Array` is used on a single thread only. Concurrent reads on
@@ -526,57 +556,41 @@ impl<T: ArrayElement> Array<T> {
526
556
result. with_cache ( self )
527
557
}
528
558
529
- /// Returns a sub-range `begin..end`, as a new array.
530
- ///
531
- /// The values of `begin` (inclusive) and `end` (exclusive) will be clamped to the array size.
532
- ///
533
- /// If specified, `step` is the relative index between source elements. It can be negative,
534
- /// in which case `begin` must be higher than `end`. For example,
535
- /// `Array::from(&[0, 1, 2, 3, 4, 5]).slice(5, 1, -2)` returns `[5, 3]`.
559
+ /// Returns a sub-range `begin..end` as a new `Array`.
536
560
///
537
561
/// Array elements are copied to the slice, but any reference types (such as `Array`,
538
562
/// `Dictionary` and `Object`) will still refer to the same value. To create a deep copy, use
539
563
/// [`subarray_deep()`][Self::subarray_deep] instead.
540
564
///
541
565
/// _Godot equivalent: `slice`_
542
566
#[ doc( alias = "slice" ) ]
543
- // TODO(v0.3): change to i32 like NodePath::slice/subpath() and support+test negative indices.
544
- pub fn subarray_shallow ( & self , begin : usize , end : usize , step : Option < isize > ) -> Self {
545
- self . subarray_impl ( begin, end, step, false )
567
+ pub fn subarray_shallow ( & self , range : impl SignedRange , step : Option < i32 > ) -> Self {
568
+ self . subarray_impl ( range, step, false )
546
569
}
547
570
548
- /// Returns a sub-range `begin..end`, as a new `Array`.
549
- ///
550
- /// The values of `begin` (inclusive) and `end` (exclusive) will be clamped to the array size.
551
- ///
552
- /// If specified, `step` is the relative index between source elements. It can be negative,
553
- /// in which case `begin` must be higher than `end`. For example,
554
- /// `Array::from(&[0, 1, 2, 3, 4, 5]).slice(5, 1, -2)` returns `[5, 3]`.
571
+ /// Returns a sub-range `begin..end` as a new `Array`.
555
572
///
556
573
/// All nested arrays and dictionaries are duplicated and will not be shared with the original
557
574
/// array. Note that any `Object`-derived elements will still be shallow copied. To create a
558
575
/// shallow copy, use [`subarray_shallow()`][Self::subarray_shallow] instead.
559
576
///
560
577
/// _Godot equivalent: `slice`_
561
578
#[ doc( alias = "slice" ) ]
562
- // TODO(v0.3): change to i32 like NodePath::slice/subpath() and support+test negative indices.
563
- pub fn subarray_deep ( & self , begin : usize , end : usize , step : Option < isize > ) -> Self {
564
- self . subarray_impl ( begin, end, step, true )
579
+ pub fn subarray_deep ( & self , range : impl SignedRange , step : Option < i32 > ) -> Self {
580
+ self . subarray_impl ( range, step, true )
565
581
}
566
582
567
- fn subarray_impl ( & self , begin : usize , end : usize , step : Option < isize > , deep : bool ) -> Self {
583
+ // Note: Godot will clamp values by itself.
584
+ fn subarray_impl ( & self , range : impl SignedRange , step : Option < i32 > , deep : bool ) -> Self {
568
585
assert_ne ! ( step, Some ( 0 ) , "subarray: step cannot be zero" ) ;
569
586
570
- let len = self . len ( ) ;
571
- let begin = begin. min ( len) ;
572
- let end = end. min ( len) ;
573
587
let step = step. unwrap_or ( 1 ) ;
588
+ let ( begin, end) = range. signed ( ) ;
589
+ let end = end. unwrap_or ( i32:: MAX as i64 ) ;
574
590
575
591
// SAFETY: The type of the array is `T` and we convert the returned array to an `Array<T>` immediately.
576
- let subarray: VariantArray = unsafe {
577
- self . as_inner ( )
578
- . slice ( to_i64 ( begin) , to_i64 ( end) , step. try_into ( ) . unwrap ( ) , deep)
579
- } ;
592
+ let subarray: VariantArray =
593
+ unsafe { self . as_inner ( ) . slice ( begin, end, step as i64 , deep) } ;
580
594
581
595
// SAFETY: slice() returns a typed array with the same type as Self.
582
596
let result = unsafe { subarray. assume_type ( ) } ;
0 commit comments