diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index 354ad6b7b7333..77da3e6318a8f 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -181,6 +181,9 @@ pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; #[stable(feature = "inclusive_range", since = "1.26.0")] pub use self::range::{Bound, RangeBounds, RangeInclusive, RangeToInclusive}; +#[unstable(feature = "one_sided_range", issue = "69780")] +pub use self::range::OneSidedRange; + #[unstable(feature = "try_trait", issue = "42327")] pub use self::r#try::Try; diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 1d67e65e51f5f..3a057d4146723 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -1004,3 +1004,21 @@ impl RangeBounds for RangeToInclusive<&T> { Included(self.end) } } + +/// `OneSidedRange` is implemented by Rust's built-in range types which +/// are unbounded on one side. For example, `a..`, `..b` and `..=c` implement +/// `OneSidedRange`, but `..`, `d..e`, and `f..=g` do not. +/// +/// Types which implement `OneSidedRange` must return `Bound::Unbounded` +/// from exactly one of `RangeBounds::start_bound` and `RangeBounds::end_bound`. +#[unstable(feature = "one_sided_range", issue = "69780")] +pub trait OneSidedRange: RangeBounds {} + +#[unstable(feature = "one_sided_range", issue = "69780")] +impl OneSidedRange for RangeTo where Self: RangeBounds {} + +#[unstable(feature = "one_sided_range", issue = "69780")] +impl OneSidedRange for RangeFrom where Self: RangeBounds {} + +#[unstable(feature = "one_sided_range", issue = "69780")] +impl OneSidedRange for RangeToInclusive where Self: RangeBounds {} diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 79ae1d5829a24..ef936af229a82 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -12,7 +12,7 @@ use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::marker::Copy; use crate::mem; use crate::num::NonZeroUsize; -use crate::ops::{FnMut, Range, RangeBounds}; +use crate::ops::{Bound, FnMut, OneSidedRange, Range, RangeBounds}; use crate::option::Option; use crate::option::Option::{None, Some}; use crate::ptr; @@ -73,6 +73,24 @@ pub use sort::heapsort; #[stable(feature = "slice_get_slice", since = "1.28.0")] pub use index::SliceIndex; +/// Calculates the direction and split point of a one-sided range. +/// +/// Helper for `take` and `take_mut` which returns a boolean +/// indicating whether the front of the split is being taken +/// (as opposed to the back), as well as a number indicating the +/// index at which to split. Returns `None` if the split index would +/// overflow `usize`. +#[inline] +fn take_split_point(range: impl OneSidedRange) -> Option<(bool, usize)> { + Some(match (range.start_bound(), range.end_bound()) { + (Bound::Unbounded, Bound::Excluded(i)) => (true, *i), + (Bound::Unbounded, Bound::Included(i)) => (true, i.checked_add(1)?), + (Bound::Excluded(i), Bound::Unbounded) => (false, i.checked_add(1)?), + (Bound::Included(i), Bound::Unbounded) => (false, *i), + _ => unreachable!(), + }) +} + #[lang = "slice"] #[cfg(not(test))] impl [T] { @@ -3169,6 +3187,232 @@ impl [T] { left } + + /// Returns the subslice corresponding to the given range, + /// and modifies the slice to no longer include this subslice. + /// + /// Returns `None` and does not modify the slice if the given + /// range is out of bounds. + /// + /// # Examples + /// + /// Taking the first three items from a slice (via `..3`): + /// + /// ``` + /// #![feature(slice_take)] + /// + /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; + /// let mut first_three = slice.take(..3).unwrap(); + /// + /// assert_eq!(slice, &['d']); + /// assert_eq!(first_three, &['a', 'b', 'c']); + /// ``` + /// + /// Taking the tail of a slice starting at index two (via `2..`): + /// + /// ``` + /// #![feature(slice_take)] + /// + /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; + /// let mut tail = slice.take(2..).unwrap(); + /// + /// assert_eq!(slice, &['a', 'b']); + /// assert_eq!(tail, &['c', 'd']); + /// ``` + /// + /// Getting `None` when `range` starts or ends outside of the slice: + /// + /// ``` + /// #![feature(slice_take)] + /// + /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; + /// + /// assert_eq!(None, slice.take(5..)); + /// assert_eq!(None, slice.take(..5)); + /// assert_eq!(None, slice.take(..=4)); + /// let expected: &[char] = &['a', 'b', 'c', 'd']; + /// assert_eq!(Some(expected), slice.take(..4)); + /// ``` + #[inline] + #[unstable(feature = "slice_take", issue = "62280")] + pub fn take<'a, R: OneSidedRange>(self: &mut &'a Self, range: R) -> Option<&'a Self> { + let (taking_front, split_index) = take_split_point(range)?; + if split_index > self.len() { + return None; + } + let (front, back) = self.split_at(split_index); + if taking_front { + *self = back; + Some(front) + } else { + *self = front; + Some(back) + } + } + + /// Returns the mutable subslice corresponding to the given range, + /// and modifies the slice to no longer include this subslice. + /// + /// Returns `None` and does not modify the slice if the given + /// range is out of bounds. + /// + /// # Examples + /// + /// Taking the first three items from a slice (via `..3`): + /// + /// ``` + /// #![feature(slice_take)] + /// + /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; + /// let mut first_three = slice.take_mut(..3).unwrap(); + /// + /// assert_eq!(slice, &mut ['d']); + /// assert_eq!(first_three, &mut ['a', 'b', 'c']); + /// ``` + /// + /// Taking the tail of a slice starting at index two (via `2..`): + /// + /// ``` + /// #![feature(slice_take)] + /// + /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; + /// let mut tail = slice.take_mut(2..).unwrap(); + /// + /// assert_eq!(slice, &mut ['a', 'b']); + /// assert_eq!(tail, &mut ['c', 'd']); + /// ``` + /// + /// Getting `None` when `range` starts or ends outside of the slice: + /// + /// ``` + /// #![feature(slice_take)] + /// + /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; + /// + /// assert_eq!(None, slice.take_mut(5..)); + /// assert_eq!(None, slice.take_mut(..5)); + /// assert_eq!(None, slice.take_mut(..=4)); + /// let expected: &mut [_] = &mut ['a', 'b', 'c', 'd']; + /// assert_eq!(Some(expected), slice.take_mut(..4)); + /// ``` + #[inline] + #[unstable(feature = "slice_take", issue = "62280")] + pub fn take_mut<'a, R: OneSidedRange>( + self: &mut &'a mut Self, + range: R, + ) -> Option<&'a mut Self> { + let (taking_front, split_index) = take_split_point(range)?; + if split_index > self.len() { + return None; + } + let original = crate::mem::take(self); + let (front, back) = original.split_at_mut(split_index); + if taking_front { + *self = back; + Some(front) + } else { + *self = front; + Some(back) + } + } + + /// Returns a reference to the first element of the slice, + /// and modifies the slice to no longer include this element. + /// + /// Returns `None` if the slice is empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_take)] + /// + /// let mut slice: &[_] = &['a', 'b', 'c']; + /// let first = slice.take_first().unwrap(); + /// + /// assert_eq!(slice, &['b', 'c']); + /// assert_eq!(first, &'a'); + /// ``` + #[inline] + #[unstable(feature = "slice_take", issue = "62280")] + pub fn take_first<'a>(self: &mut &'a Self) -> Option<&'a T> { + let (first, rem) = self.split_first()?; + *self = rem; + Some(first) + } + + /// Returns a mutable reference to the first element of the slice, + /// and modifies the slice to no longer include this element. + /// + /// Returns `None` if the slice is empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_take)] + /// + /// let mut slice: &mut [_] = &mut ['a', 'b', 'c']; + /// let first = slice.take_first_mut().unwrap(); + /// *first = 'd'; + /// + /// assert_eq!(slice, &['b', 'c']); + /// assert_eq!(first, &'d'); + /// ``` + #[inline] + #[unstable(feature = "slice_take", issue = "62280")] + pub fn take_first_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { + let (first, rem) = mem::take(self).split_first_mut()?; + *self = rem; + Some(first) + } + + /// Returns a reference to the last element of the slice, + /// and modifies the slice to no longer include this element. + /// + /// Returns `None` if the slice is empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_take)] + /// + /// let mut slice: &[_] = &['a', 'b', 'c']; + /// let last = slice.take_last().unwrap(); + /// + /// assert_eq!(slice, &['a', 'b']); + /// assert_eq!(last, &'c'); + /// ``` + #[inline] + #[unstable(feature = "slice_take", issue = "62280")] + pub fn take_last<'a>(self: &mut &'a Self) -> Option<&'a T> { + let (last, rem) = self.split_last()?; + *self = rem; + Some(last) + } + + /// Returns a mutable reference to the last element of the slice, + /// and modifies the slice to no longer include this element. + /// + /// Returns `None` if the slice is empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_take)] + /// + /// let mut slice: &mut [_] = &mut ['a', 'b', 'c']; + /// let last = slice.take_last_mut().unwrap(); + /// *last = 'd'; + /// + /// assert_eq!(slice, &['a', 'b']); + /// assert_eq!(last, &'d'); + /// ``` + #[inline] + #[unstable(feature = "slice_take", issue = "62280")] + pub fn take_last_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { + let (last, rem) = mem::take(self).split_last_mut()?; + *self = rem; + Some(last) + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 0c4ce867f542d..51987de0398d3 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -39,6 +39,7 @@ #![feature(try_trait)] #![feature(slice_internals)] #![feature(slice_partition_dedup)] +#![feature(slice_take)] #![feature(int_error_matching)] #![feature(array_value_iter)] #![feature(iter_advance_by)] diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index 9ccc5a08dcbea..ac3c1e866e524 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -2038,3 +2038,83 @@ fn test_slice_run_destructors() { assert_eq!(x.get(), 1); } + +macro_rules! take_tests { + (slice: &$slice:expr, $($tts:tt)*) => { + take_tests!(ty: &[_], slice: &$slice, $($tts)*); + }; + (slice: &mut $slice:expr, $($tts:tt)*) => { + take_tests!(ty: &mut [_], slice: &mut $slice, $($tts)*); + }; + (ty: $ty:ty, slice: $slice:expr, method: $method:ident, $(($test_name:ident, ($($args:expr),*), $output:expr, $remaining:expr),)*) => { + $( + #[test] + fn $test_name() { + let mut slice: $ty = $slice; + assert_eq!($output, slice.$method($($args)*)); + let remaining: $ty = $remaining; + assert_eq!(remaining, slice); + } + )* + }; +} + +take_tests! { + slice: &[0, 1, 2, 3], method: take, + (take_in_bounds_range_to, (..1), Some(&[0] as _), &[1, 2, 3]), + (take_in_bounds_range_to_inclusive, (..=0), Some(&[0] as _), &[1, 2, 3]), + (take_in_bounds_range_from, (2..), Some(&[2, 3] as _), &[0, 1]), + (take_oob_range_to, (..5), None, &[0, 1, 2, 3]), + (take_oob_range_to_inclusive, (..=4), None, &[0, 1, 2, 3]), + (take_oob_range_from, (5..), None, &[0, 1, 2, 3]), +} + +take_tests! { + slice: &mut [0, 1, 2, 3], method: take_mut, + (take_mut_in_bounds_range_to, (..1), Some(&mut [0] as _), &mut [1, 2, 3]), + (take_mut_in_bounds_range_to_inclusive, (..=0), Some(&mut [0] as _), &mut [1, 2, 3]), + (take_mut_in_bounds_range_from, (2..), Some(&mut [2, 3] as _), &mut [0, 1]), + (take_mut_oob_range_to, (..5), None, &mut [0, 1, 2, 3]), + (take_mut_oob_range_to_inclusive, (..=4), None, &mut [0, 1, 2, 3]), + (take_mut_oob_range_from, (5..), None, &mut [0, 1, 2, 3]), +} + +take_tests! { + ty: &[_], slice: &[1, 2], method: take_first, + (take_first_nonempty, (), Some(&1), &[2]), +} + +take_tests! { + ty: &mut [_], slice: &mut [1, 2], method: take_first_mut, + (take_first_mut_nonempty, (), Some(&mut 1), &mut [2]), +} + +take_tests! { + ty: &[_], slice: &[1, 2], method: take_last, + (take_last_nonempty, (), Some(&2), &[1]), +} + +take_tests! { + ty: &mut [_], slice: &mut [1, 2], method: take_last_mut, + (take_last_mut_nonempty, (), Some(&mut 2), &mut [1]), +} + +take_tests! { + ty: &[()], slice: &[], method: take_first, + (take_first_empty, (), None, &[]), +} + +take_tests! { + ty: &mut [()], slice: &mut [], method: take_first_mut, + (take_first_mut_empty, (), None, &mut []), +} + +take_tests! { + ty: &[()], slice: &[], method: take_last, + (take_last_empty, (), None, &[]), +} + +take_tests! { + ty: &mut [()], slice: &mut [], method: take_last_mut, + (take_last_mut_empty, (), None, &mut []), +}