about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--library/core/src/slice/mod.rs93
1 files changed, 87 insertions, 6 deletions
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index 93608a1ce48..d5e07629a52 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -30,7 +30,7 @@ use crate::intrinsics::{assume, exact_div, is_aligned_and_not_null, unchecked_su
 use crate::iter::*;
 use crate::marker::{self, Copy, Send, Sized, Sync};
 use crate::mem;
-use crate::ops::{self, FnMut, Range};
+use crate::ops::{self, Bound, FnMut, Range, RangeBounds};
 use crate::option::Option;
 use crate::option::Option::{None, Some};
 use crate::ptr::{self, NonNull};
@@ -350,6 +350,80 @@ impl<T> [T] {
         unsafe { &mut *index.get_unchecked_mut(self) }
     }
 
+    /// Converts a range over this slice to [`Range`].
+    ///
+    /// The returned range is safe to pass to [`get_unchecked`] and [`get_unchecked_mut`].
+    ///
+    /// [`get_unchecked`]: #method.get_unchecked
+    /// [`get_unchecked_mut`]: #method.get_unchecked_mut
+    /// [`Range`]: ../ops/struct.Range.html
+    ///
+    /// # Panics
+    ///
+    /// Panics if the range is out of bounds.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(slice_check_range)]
+    ///
+    /// let v = [10, 40, 30];
+    /// assert_eq!(1..2, v.check_range(1..2));
+    /// assert_eq!(0..2, v.check_range(..2));
+    /// assert_eq!(1..3, v.check_range(1..));
+    /// ```
+    ///
+    /// Panics when [`Index::index`] would panic:
+    ///
+    /// ```should_panic
+    /// #![feature(slice_check_range)]
+    ///
+    /// [10, 40, 30].check_range(2..1);
+    /// ```
+    ///
+    /// ```should_panic
+    /// #![feature(slice_check_range)]
+    ///
+    /// [10, 40, 30].check_range(1..4);
+    /// ```
+    ///
+    /// ```should_panic
+    /// #![feature(slice_check_range)]
+    ///
+    /// [10, 40, 30].check_range(1..=usize::MAX);
+    /// ```
+    ///
+    /// [`Index::index`]: ../ops/trait.Index.html#tymethod.index
+    #[track_caller]
+    #[unstable(feature = "slice_check_range", issue = "none")]
+    pub fn check_range<R: RangeBounds<usize>>(&self, range: R) -> Range<usize> {
+        let start = match range.start_bound() {
+            Bound::Included(&start) => start,
+            Bound::Excluded(start) => {
+                start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail())
+            }
+            Bound::Unbounded => 0,
+        };
+
+        let len = self.len();
+        let end = match range.end_bound() {
+            Bound::Included(end) => {
+                end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail())
+            }
+            Bound::Excluded(&end) => end,
+            Bound::Unbounded => len,
+        };
+
+        if start > end {
+            slice_index_order_fail(start, end);
+        }
+        if end > len {
+            slice_end_index_len_fail(end, len);
+        }
+
+        Range { start, end }
+    }
+
     /// Returns a raw pointer to the slice's buffer.
     ///
     /// The caller must ensure that the slice outlives the pointer this
@@ -2445,13 +2519,13 @@ impl<T> [T] {
         let src_start = match src.start_bound() {
             ops::Bound::Included(&n) => n,
             ops::Bound::Excluded(&n) => {
-                n.checked_add(1).unwrap_or_else(|| slice_index_overflow_fail())
+                n.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail())
             }
             ops::Bound::Unbounded => 0,
         };
         let src_end = match src.end_bound() {
             ops::Bound::Included(&n) => {
-                n.checked_add(1).unwrap_or_else(|| slice_index_overflow_fail())
+                n.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail())
             }
             ops::Bound::Excluded(&n) => n,
             ops::Bound::Unbounded => self.len(),
@@ -3034,7 +3108,14 @@ fn slice_index_order_fail(index: usize, end: usize) -> ! {
 #[inline(never)]
 #[cold]
 #[track_caller]
-fn slice_index_overflow_fail() -> ! {
+fn slice_start_index_overflow_fail() -> ! {
+    panic!("attempted to index slice from after maximum usize");
+}
+
+#[inline(never)]
+#[cold]
+#[track_caller]
+fn slice_end_index_overflow_fail() -> ! {
     panic!("attempted to index slice up to maximum usize");
 }
 
@@ -3370,7 +3451,7 @@ unsafe impl<T> SliceIndex<[T]> for ops::RangeInclusive<usize> {
     #[inline]
     fn index(self, slice: &[T]) -> &[T] {
         if *self.end() == usize::MAX {
-            slice_index_overflow_fail();
+            slice_end_index_overflow_fail();
         }
         (*self.start()..self.end() + 1).index(slice)
     }
@@ -3378,7 +3459,7 @@ unsafe impl<T> SliceIndex<[T]> for ops::RangeInclusive<usize> {
     #[inline]
     fn index_mut(self, slice: &mut [T]) -> &mut [T] {
         if *self.end() == usize::MAX {
-            slice_index_overflow_fail();
+            slice_end_index_overflow_fail();
         }
         (*self.start()..self.end() + 1).index_mut(slice)
     }