about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJacob Pratt <jacob@jhpratt.dev>2024-03-11 03:47:18 -0400
committerGitHub <noreply@github.com>2024-03-11 03:47:18 -0400
commit6c8c272ad4211853b2b7fe97edebf1e5fa2095bb (patch)
tree43d9c4e4d3c161daa83d4f1cc2183bde47cfb5e3
parenta6d93acf5fdeb020ab86cc0d30d5672c23a7dba6 (diff)
parent290cbdf50ed7aa856a6794178500c7bd76fc7ec9 (diff)
downloadrust-6c8c272ad4211853b2b7fe97edebf1e5fa2095bb.tar.gz
rust-6c8c272ad4211853b2b7fe97edebf1e5fa2095bb.zip
Rollup merge of #121148 - clarfonthey:try-range, r=dtolnay
Add slice::try_range

This adds a fallible version of the unstable `slice::range` (tracking: #76393) which is highly requested in the tracking issue.

Hoping this can slide by without an ACP (since the feature is already being tracked), but let me know otherwise.
-rw-r--r--library/alloc/src/slice.rs4
-rw-r--r--library/core/src/slice/index.rs59
-rw-r--r--library/core/src/slice/mod.rs2
3 files changed, 58 insertions, 7 deletions
diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs
index f4e392760c8..21d5dce04a0 100644
--- a/library/alloc/src/slice.rs
+++ b/library/alloc/src/slice.rs
@@ -33,8 +33,6 @@ use crate::vec::Vec;
 #[cfg(test)]
 mod tests;
 
-#[unstable(feature = "slice_range", issue = "76393")]
-pub use core::slice::range;
 #[unstable(feature = "array_chunks", issue = "74985")]
 pub use core::slice::ArrayChunks;
 #[unstable(feature = "array_chunks", issue = "74985")]
@@ -51,6 +49,8 @@ pub use core::slice::{from_mut, from_ref};
 pub use core::slice::{from_mut_ptr_range, from_ptr_range};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::slice::{from_raw_parts, from_raw_parts_mut};
+#[unstable(feature = "slice_range", issue = "76393")]
+pub use core::slice::{range, try_range};
 #[stable(feature = "slice_group_by", since = "1.77.0")]
 pub use core::slice::{ChunkBy, ChunkByMut};
 #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs
index 33c2769591d..64f1f360821 100644
--- a/library/core/src/slice/index.rs
+++ b/library/core/src/slice/index.rs
@@ -704,8 +704,7 @@ where
 {
     let len = bounds.end;
 
-    let start: ops::Bound<&usize> = range.start_bound();
-    let start = match start {
+    let start = match range.start_bound() {
         ops::Bound::Included(&start) => start,
         ops::Bound::Excluded(start) => {
             start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail())
@@ -713,8 +712,7 @@ where
         ops::Bound::Unbounded => 0,
     };
 
-    let end: ops::Bound<&usize> = range.end_bound();
-    let end = match end {
+    let end = match range.end_bound() {
         ops::Bound::Included(end) => {
             end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail())
         }
@@ -732,6 +730,59 @@ where
     ops::Range { start, end }
 }
 
+/// Performs bounds-checking of a range without panicking.
+///
+/// This is a version of [`range`] that returns [`None`] instead of panicking.
+///
+/// # Examples
+///
+/// ```
+/// #![feature(slice_range)]
+///
+/// use std::slice;
+///
+/// let v = [10, 40, 30];
+/// assert_eq!(Some(1..2), slice::try_range(1..2, ..v.len()));
+/// assert_eq!(Some(0..2), slice::try_range(..2, ..v.len()));
+/// assert_eq!(Some(1..3), slice::try_range(1.., ..v.len()));
+/// ```
+///
+/// Returns [`None`] when [`Index::index`] would panic:
+///
+/// ```
+/// #![feature(slice_range)]
+///
+/// use std::slice;
+///
+/// assert_eq!(None, slice::try_range(2..1, ..3));
+/// assert_eq!(None, slice::try_range(1..4, ..3));
+/// assert_eq!(None, slice::try_range(1..=usize::MAX, ..3));
+/// ```
+///
+/// [`Index::index`]: ops::Index::index
+#[unstable(feature = "slice_range", issue = "76393")]
+#[must_use]
+pub fn try_range<R>(range: R, bounds: ops::RangeTo<usize>) -> Option<ops::Range<usize>>
+where
+    R: ops::RangeBounds<usize>,
+{
+    let len = bounds.end;
+
+    let start = match range.start_bound() {
+        ops::Bound::Included(&start) => start,
+        ops::Bound::Excluded(start) => start.checked_add(1)?,
+        ops::Bound::Unbounded => 0,
+    };
+
+    let end = match range.end_bound() {
+        ops::Bound::Included(end) => end.checked_add(1)?,
+        ops::Bound::Excluded(&end) => end,
+        ops::Bound::Unbounded => len,
+    };
+
+    if start > end || end > len { None } else { Some(ops::Range { start, end }) }
+}
+
 /// Convert pair of `ops::Bound`s into `ops::Range` without performing any bounds checking and (in debug) overflow checking
 pub(crate) fn into_range_unchecked(
     len: usize,
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index 2dd01ba33af..4a574bf0347 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -91,7 +91,7 @@ pub use sort::heapsort;
 pub use index::SliceIndex;
 
 #[unstable(feature = "slice_range", issue = "76393")]
-pub use index::range;
+pub use index::{range, try_range};
 
 #[stable(feature = "inherent_ascii_escape", since = "1.60.0")]
 pub use ascii::EscapeAscii;