diff --git a/src/libcore/ops/mod.rs b/src/libcore/ops/mod.rs index e3e5934b44be1..510756e5d2e07 100644 --- a/src/libcore/ops/mod.rs +++ b/src/libcore/ops/mod.rs @@ -186,6 +186,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/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index adee8cea442b4..2ce91bb137e18 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -891,3 +891,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/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 2140a7be9efe8..c54b00bf37376 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -31,7 +31,7 @@ use crate::isize; 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, OneSidedRange, Range}; use crate::option::Option; use crate::option::Option::{None, Some}; use crate::ptr::{self, NonNull}; @@ -53,6 +53,24 @@ mod sort; // Extension traits // +/// 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] { @@ -2639,6 +2657,227 @@ impl [T] { { self.iter().is_sorted_by_key(f) } + + /// Removes and returns the portion of the slice specified by `range`. + /// + /// If the provided `range` starts or ends outside of the slice, + /// `None` is returned and the slice is not modified. + /// + /// # 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 original = crate::mem::take(self); + let (front, back) = original.split_at(split_index); + if taking_front { + *self = back; + Some(front) + } else { + *self = front; + Some(back) + } + } + + /// Removes and returns the portion of the mutable slice specified by `range`. + /// + /// If the provided `range` starts or ends outside of the slice, + /// `None` is returned and the slice is not modified. + /// + /// # 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) + } + } + + /// Takes the first element out of the slice. + /// + /// Returns a reference pointing to the first element of the old slice. + /// + /// 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> { + self.take(..=0).map(|res| &res[0]) + } + + /// Takes the first element out of the mutable slice. + /// + /// Returns a mutable reference pointing to the first element of the old slice. + /// + /// 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> { + self.take_mut(..=0).map(|res| &mut res[0]) + } + + /// Takes the last element out of the slice. + /// + /// Returns a reference pointing to the last element of the old slice. + /// + /// 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> { + self.take((self.len() - 1)..).map(|res| &res[0]) + } + + /// Takes the last element out of the mutable slice. + /// + /// Returns a mutable reference pointing to the last element of the old slice. + /// + /// 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> { + self.take_mut((self.len() - 1)..).map(|res| &mut res[0]) + } } #[lang = "slice_u8"] diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 05f958cbe81fe..ed5627b4d2c2b 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -42,6 +42,7 @@ #![feature(unwrap_infallible)] #![feature(leading_trailing_ones)] #![feature(const_forget)] +#![feature(slice_take)] extern crate test; diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index dbab433e33f48..a73e0495444d1 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -1715,3 +1715,103 @@ fn test_is_sorted() { assert!(!["c", "bb", "aaa"].is_sorted()); assert!(["c", "bb", "aaa"].is_sorted_by_key(|s| s.len())); } + +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 []), +} + +const EMPTY_MAX: &'static [()] = &[(); std::usize::MAX]; + +// can't be a constant due to const mutability rules +// see https://github.com/rust-lang/rust/issues/57349#issuecomment-597395059 +macro_rules! empty_max_mut { () => { &mut [(); std::usize::MAX] as _ } } + +take_tests! { + slice: &[(); ::std::usize::MAX], method: take, + (take_in_bounds_max_range_to, (..::std::usize::MAX), Some(EMPTY_MAX), &[(); 0]), + (take_oob_max_range_to_inclusive, (..=::std::usize::MAX), None, EMPTY_MAX), + (take_in_bounds_max_range_from, (::std::usize::MAX..), Some(&[] as _), EMPTY_MAX), +} + +take_tests! { + slice: &mut [(); ::std::usize::MAX], method: take_mut, + (take_mut_in_bounds_max_range_to, (..::std::usize::MAX), Some(empty_max_mut!()), &mut [(); 0]), + (take_mut_oob_max_range_to_inclusive, (..=::std::usize::MAX), None, empty_max_mut!()), + (take_mut_in_bounds_max_range_from, (::std::usize::MAX..), Some(&mut [] as _), empty_max_mut!()), +}