diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index f4e392760c8e4..21d5dce04a0fc 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 fb9be396eab80..c34d616e268f9 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -681,8 +681,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()) @@ -690,8 +689,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()) } @@ -709,6 +707,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(range: R, bounds: ops::RangeTo) -> Option> +where + R: ops::RangeBounds, +{ + 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 73e92ed1dad63..aa228e12325ef 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;