diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2023-05-25 08:01:07 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-05-25 08:01:07 +0200 |
| commit | 8497948c7ac58dd467d280b40842b6545a9f56d0 (patch) | |
| tree | bbee038dbd70d39923aaab1bbae34f0193a9585b | |
| parent | 7664dfe4331265d0b2b1ffb89c92d443886bec0b (diff) | |
| parent | d58dd10f5ad1e0b91ee6de3b6add0e1a20f8a311 (diff) | |
| download | rust-8497948c7ac58dd467d280b40842b6545a9f56d0.tar.gz rust-8497948c7ac58dd467d280b40842b6545a9f56d0.zip | |
Rollup merge of #95198 - clarfonthey:get_chunk, r=scottmcm
Add slice::{split_,}{first,last}_chunk{,_mut}
This adds to the existing tracking issue for `slice::array_chunks` (#74985) under a separate feature, `slice_get_chunk`.
Currently, we have the existing `first`/`last` API for slices:
```rust
impl [T] {
pub const fn first(&self) -> Option<&T>;
pub const fn first_mut(&mut self) -> Option<&mut T>;
pub const fn last(&self) -> Option<&T>;
pub const fn last_mut(&mut self) -> Option<&mut T>;
pub const fn split_first(&self) -> Option<(&T, &[T])>;
pub const fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])>;
pub const fn split_last(&self) -> Option<(&T, &[T])>;
pub const fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])>;
}
```
This augments it with a `first_chunk`/`last_chunk` API that allows retrieving multiple elements at once:
```rust
impl [T] {
pub const fn first_chunk<const N: usize>(&self) -> Option<&[T; N]>;
pub const fn first_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]>;
pub const fn last_chunk<const N: usize>(&self) -> Option<&[T; N]>;
pub const fn last_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]>;
pub const fn split_first_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])>;
pub const fn split_first_chunk_mut<const N: usize>(&mut self) -> Option<(&mut [T; N], &mut [T])>;
pub const fn split_last_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])>;
pub const fn split_last_chunk_mut<const N: usize>(&mut self) -> Option<(&mut [T; N], &mut [T])>;
}
```
The code is based off of a copy of the existing API, with the documentation and examples properly modified. Currently, the most common way to perform these kinds of lookups with the existing methods is via `slice.as_chunks::<N>().0[0]` or the worse `slice.as_chunks::<N>().0[slice.len() - N]`, which is substantially less readable than `slice.first_chunk::<N>()` or `slice.last_chunk::<N>()`.
ACP: https://github.com/rust-lang/libs-team/issues/69
| -rw-r--r-- | library/core/src/slice/mod.rs | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 5ece1b78c03..c787b7288b0 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -319,6 +319,264 @@ impl<T> [T] { if let [.., last] = self { Some(last) } else { None } } + /// Returns the first `N` elements of the slice, or `None` if it has fewer than `N` elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let u = [10, 40, 30]; + /// assert_eq!(Some(&[10, 40]), u.first_chunk::<2>()); + /// + /// let v: &[i32] = &[10]; + /// assert_eq!(None, v.first_chunk::<2>()); + /// + /// let w: &[i32] = &[]; + /// assert_eq!(Some(&[]), w.first_chunk::<0>()); + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn first_chunk<const N: usize>(&self) -> Option<&[T; N]> { + if self.len() < N { + None + } else { + // SAFETY: We explicitly check for the correct number of elements, + // and do not let the reference outlive the slice. + Some(unsafe { &*(self.as_ptr() as *const [T; N]) }) + } + } + + /// Returns a mutable reference to the first `N` elements of the slice, + /// or `None` if it has fewer than `N` elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let x = &mut [0, 1, 2]; + /// + /// if let Some(first) = x.first_chunk_mut::<2>() { + /// first[0] = 5; + /// first[1] = 4; + /// } + /// assert_eq!(x, &[5, 4, 2]); + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn first_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]> { + if self.len() < N { + None + } else { + // SAFETY: We explicitly check for the correct number of elements, + // do not let the reference outlive the slice, + // and require exclusive access to the entire slice to mutate the chunk. + Some(unsafe { &mut *(self.as_mut_ptr() as *mut [T; N]) }) + } + } + + /// Returns the first `N` elements of the slice and the remainder, + /// or `None` if it has fewer than `N` elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let x = &[0, 1, 2]; + /// + /// if let Some((first, elements)) = x.split_first_chunk::<2>() { + /// assert_eq!(first, &[0, 1]); + /// assert_eq!(elements, &[2]); + /// } + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn split_first_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])> { + if self.len() < N { + None + } else { + // SAFETY: We manually verified the bounds of the split. + let (first, tail) = unsafe { self.split_at_unchecked(N) }; + + // SAFETY: We explicitly check for the correct number of elements, + // and do not let the references outlive the slice. + Some((unsafe { &*(first.as_ptr() as *const [T; N]) }, tail)) + } + } + + /// Returns a mutable reference to the first `N` elements of the slice and the remainder, + /// or `None` if it has fewer than `N` elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let x = &mut [0, 1, 2]; + /// + /// if let Some((first, elements)) = x.split_first_chunk_mut::<2>() { + /// first[0] = 3; + /// first[1] = 4; + /// elements[0] = 5; + /// } + /// assert_eq!(x, &[3, 4, 5]); + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn split_first_chunk_mut<const N: usize>( + &mut self, + ) -> Option<(&mut [T; N], &mut [T])> { + if self.len() < N { + None + } else { + // SAFETY: We manually verified the bounds of the split. + let (first, tail) = unsafe { self.split_at_mut_unchecked(N) }; + + // SAFETY: We explicitly check for the correct number of elements, + // do not let the reference outlive the slice, + // and enforce exclusive mutability of the chunk by the split. + Some((unsafe { &mut *(first.as_mut_ptr() as *mut [T; N]) }, tail)) + } + } + + /// Returns the last `N` elements of the slice and the remainder, + /// or `None` if it has fewer than `N` elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let x = &[0, 1, 2]; + /// + /// if let Some((last, elements)) = x.split_last_chunk::<2>() { + /// assert_eq!(last, &[1, 2]); + /// assert_eq!(elements, &[0]); + /// } + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn split_last_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])> { + if self.len() < N { + None + } else { + // SAFETY: We manually verified the bounds of the split. + let (init, last) = unsafe { self.split_at_unchecked(self.len() - N) }; + + // SAFETY: We explicitly check for the correct number of elements, + // and do not let the references outlive the slice. + Some((unsafe { &*(last.as_ptr() as *const [T; N]) }, init)) + } + } + + /// Returns the last and all the rest of the elements of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let x = &mut [0, 1, 2]; + /// + /// if let Some((last, elements)) = x.split_last_chunk_mut::<2>() { + /// last[0] = 3; + /// last[1] = 4; + /// elements[0] = 5; + /// } + /// assert_eq!(x, &[5, 3, 4]); + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn split_last_chunk_mut<const N: usize>( + &mut self, + ) -> Option<(&mut [T; N], &mut [T])> { + if self.len() < N { + None + } else { + // SAFETY: We manually verified the bounds of the split. + let (init, last) = unsafe { self.split_at_mut_unchecked(self.len() - N) }; + + // SAFETY: We explicitly check for the correct number of elements, + // do not let the reference outlive the slice, + // and enforce exclusive mutability of the chunk by the split. + Some((unsafe { &mut *(last.as_mut_ptr() as *mut [T; N]) }, init)) + } + } + + /// Returns the last element of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let u = [10, 40, 30]; + /// assert_eq!(Some(&[40, 30]), u.last_chunk::<2>()); + /// + /// let v: &[i32] = &[10]; + /// assert_eq!(None, v.last_chunk::<2>()); + /// + /// let w: &[i32] = &[]; + /// assert_eq!(Some(&[]), w.last_chunk::<0>()); + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn last_chunk<const N: usize>(&self) -> Option<&[T; N]> { + if self.len() < N { + None + } else { + // SAFETY: We manually verified the bounds of the slice. + // FIXME: Without const traits, we need this instead of `get_unchecked`. + let last = unsafe { self.split_at_unchecked(self.len() - N).1 }; + + // SAFETY: We explicitly check for the correct number of elements, + // and do not let the references outlive the slice. + Some(unsafe { &*(last.as_ptr() as *const [T; N]) }) + } + } + + /// Returns a mutable pointer to the last item in the slice. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let x = &mut [0, 1, 2]; + /// + /// if let Some(last) = x.last_chunk_mut::<2>() { + /// last[0] = 10; + /// last[1] = 20; + /// } + /// assert_eq!(x, &[0, 10, 20]); + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn last_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]> { + if self.len() < N { + None + } else { + // SAFETY: We manually verified the bounds of the slice. + // FIXME: Without const traits, we need this instead of `get_unchecked`. + let last = unsafe { self.split_at_mut_unchecked(self.len() - N).1 }; + + // SAFETY: We explicitly check for the correct number of elements, + // do not let the reference outlive the slice, + // and require exclusive access to the entire slice to mutate the chunk. + Some(unsafe { &mut *(last.as_mut_ptr() as *mut [T; N]) }) + } + } + /// Returns a reference to an element or subslice depending on the type of /// index. /// |
