Rollup merge of #139656 - scottmcm:stabilize-slice-as-chunks, r=dtolnay

Stabilize `slice_as_chunks` library feature

~~Draft as this needs #139163 to land first.~~

FCP: https://github.com/rust-lang/rust/issues/74985#issuecomment-2769963395

Methods being stabilized are:
```rust
impl [T] {
    const fn as_chunks<const N: usize>(&self) -> (&[[T; N]], &[T]);
    const fn as_rchunks<const N: usize>(&self) -> (&[T], &[[T; N]]);
    const unsafe fn as_chunks_unchecked<const N: usize>(&self) -> &[[T; N]];
    const fn as_chunks_mut<const N: usize>(&mut self) -> (&mut [[T; N]], &mut [T]);
    const fn as_rchunks_mut<const N: usize>(&mut self) -> (&mut [T], &mut [[T; N]]);
    const unsafe fn as_chunks_unchecked_mut<const N: usize>(&mut self) -> &mut [[T; N]];
}
```

~~(FCP's not done quite yet, but will in another day if I'm counting right.)~~ FCP Complete: https://github.com/rust-lang/rust/issues/74985#issuecomment-2797951535
This commit is contained in:
Chris Denton
2025-04-28 23:29:15 +00:00
committed by GitHub
6 changed files with 107 additions and 27 deletions

View File

@@ -31,7 +31,6 @@
#![feature(round_char_boundary)] #![feature(round_char_boundary)]
#![feature(rustc_attrs)] #![feature(rustc_attrs)]
#![feature(rustdoc_internals)] #![feature(rustdoc_internals)]
#![feature(slice_as_chunks)]
// tidy-alphabetical-end // tidy-alphabetical-end
// The code produced by the `Encodable`/`Decodable` derive macros refer to // The code produced by the `Encodable`/`Decodable` derive macros refer to

View File

@@ -118,7 +118,6 @@
#![feature(ptr_metadata)] #![feature(ptr_metadata)]
#![feature(set_ptr_value)] #![feature(set_ptr_value)]
#![feature(slice_as_array)] #![feature(slice_as_array)]
#![feature(slice_as_chunks)]
#![feature(slice_ptr_get)] #![feature(slice_ptr_get)]
#![feature(str_internals)] #![feature(str_internals)]
#![feature(str_split_inclusive_remainder)] #![feature(str_split_inclusive_remainder)]

View File

@@ -2335,7 +2335,6 @@ pub struct ArrayChunks<'a, T: 'a, const N: usize> {
impl<'a, T, const N: usize> ArrayChunks<'a, T, N> { impl<'a, T, const N: usize> ArrayChunks<'a, T, N> {
#[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")]
// #[rustc_const_unstable(feature = "slice_as_chunks", issue = "74985")]
#[inline] #[inline]
pub(super) const fn new(slice: &'a [T]) -> Self { pub(super) const fn new(slice: &'a [T]) -> Self {
let (array_slice, rem) = slice.as_chunks(); let (array_slice, rem) = slice.as_chunks();

View File

@@ -1268,6 +1268,18 @@ impl<T> [T] {
/// Splits the slice into a slice of `N`-element arrays, /// Splits the slice into a slice of `N`-element arrays,
/// assuming that there's no remainder. /// assuming that there's no remainder.
/// ///
/// This is the inverse operation to [`as_flattened`].
///
/// [`as_flattened`]: slice::as_flattened
///
/// As this is `unsafe`, consider whether you could use [`as_chunks`] or
/// [`as_rchunks`] instead, perhaps via something like
/// `if let (chunks, []) = slice.as_chunks()` or
/// `let (chunks, []) = slice.as_chunks() else { unreachable!() };`.
///
/// [`as_chunks`]: slice::as_chunks
/// [`as_rchunks`]: slice::as_rchunks
///
/// # Safety /// # Safety
/// ///
/// This may only be called when /// This may only be called when
@@ -1277,7 +1289,6 @@ impl<T> [T] {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// #![feature(slice_as_chunks)]
/// let slice: &[char] = &['l', 'o', 'r', 'e', 'm', '!']; /// let slice: &[char] = &['l', 'o', 'r', 'e', 'm', '!'];
/// let chunks: &[[char; 1]] = /// let chunks: &[[char; 1]] =
/// // SAFETY: 1-element chunks never have remainder /// // SAFETY: 1-element chunks never have remainder
@@ -1292,7 +1303,8 @@ impl<T> [T] {
/// // let chunks: &[[_; 5]] = slice.as_chunks_unchecked() // The slice length is not a multiple of 5 /// // let chunks: &[[_; 5]] = slice.as_chunks_unchecked() // The slice length is not a multiple of 5
/// // let chunks: &[[_; 0]] = slice.as_chunks_unchecked() // Zero-length chunks are never allowed /// // let chunks: &[[_; 0]] = slice.as_chunks_unchecked() // Zero-length chunks are never allowed
/// ``` /// ```
#[unstable(feature = "slice_as_chunks", issue = "74985")] #[stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")]
#[inline] #[inline]
#[must_use] #[must_use]
pub const unsafe fn as_chunks_unchecked<const N: usize>(&self) -> &[[T; N]] { pub const unsafe fn as_chunks_unchecked<const N: usize>(&self) -> &[[T; N]] {
@@ -1312,15 +1324,27 @@ impl<T> [T] {
/// starting at the beginning of the slice, /// starting at the beginning of the slice,
/// and a remainder slice with length strictly less than `N`. /// and a remainder slice with length strictly less than `N`.
/// ///
/// The remainder is meaningful in the division sense. Given
/// `let (chunks, remainder) = slice.as_chunks()`, then:
/// - `chunks.len()` equals `slice.len() / N`,
/// - `remainder.len()` equals `slice.len() % N`, and
/// - `slice.len()` equals `chunks.len() * N + remainder.len()`.
///
/// You can flatten the chunks back into a slice-of-`T` with [`as_flattened`].
///
/// [`as_flattened`]: slice::as_flattened
///
/// # Panics /// # Panics
/// ///
/// Panics if `N` is zero. This check will most probably get changed to a compile time /// Panics if `N` is zero.
/// error before this method gets stabilized. ///
/// Note that this check is against a const generic parameter, not a runtime
/// value, and thus a particular monomorphization will either always panic
/// or it will never panic.
/// ///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// #![feature(slice_as_chunks)]
/// let slice = ['l', 'o', 'r', 'e', 'm']; /// let slice = ['l', 'o', 'r', 'e', 'm'];
/// let (chunks, remainder) = slice.as_chunks(); /// let (chunks, remainder) = slice.as_chunks();
/// assert_eq!(chunks, &[['l', 'o'], ['r', 'e']]); /// assert_eq!(chunks, &[['l', 'o'], ['r', 'e']]);
@@ -1330,14 +1354,14 @@ impl<T> [T] {
/// If you expect the slice to be an exact multiple, you can combine /// If you expect the slice to be an exact multiple, you can combine
/// `let`-`else` with an empty slice pattern: /// `let`-`else` with an empty slice pattern:
/// ``` /// ```
/// #![feature(slice_as_chunks)]
/// let slice = ['R', 'u', 's', 't']; /// let slice = ['R', 'u', 's', 't'];
/// let (chunks, []) = slice.as_chunks::<2>() else { /// let (chunks, []) = slice.as_chunks::<2>() else {
/// panic!("slice didn't have even length") /// panic!("slice didn't have even length")
/// }; /// };
/// assert_eq!(chunks, &[['R', 'u'], ['s', 't']]); /// assert_eq!(chunks, &[['R', 'u'], ['s', 't']]);
/// ``` /// ```
#[unstable(feature = "slice_as_chunks", issue = "74985")] #[stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")]
#[inline] #[inline]
#[track_caller] #[track_caller]
#[must_use] #[must_use]
@@ -1357,21 +1381,34 @@ impl<T> [T] {
/// starting at the end of the slice, /// starting at the end of the slice,
/// and a remainder slice with length strictly less than `N`. /// and a remainder slice with length strictly less than `N`.
/// ///
/// The remainder is meaningful in the division sense. Given
/// `let (remainder, chunks) = slice.as_rchunks()`, then:
/// - `remainder.len()` equals `slice.len() % N`,
/// - `chunks.len()` equals `slice.len() / N`, and
/// - `slice.len()` equals `chunks.len() * N + remainder.len()`.
///
/// You can flatten the chunks back into a slice-of-`T` with [`as_flattened`].
///
/// [`as_flattened`]: slice::as_flattened
///
/// # Panics /// # Panics
/// ///
/// Panics if `N` is zero. This check will most probably get changed to a compile time /// Panics if `N` is zero.
/// error before this method gets stabilized. ///
/// Note that this check is against a const generic parameter, not a runtime
/// value, and thus a particular monomorphization will either always panic
/// or it will never panic.
/// ///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// #![feature(slice_as_chunks)]
/// let slice = ['l', 'o', 'r', 'e', 'm']; /// let slice = ['l', 'o', 'r', 'e', 'm'];
/// let (remainder, chunks) = slice.as_rchunks(); /// let (remainder, chunks) = slice.as_rchunks();
/// assert_eq!(remainder, &['l']); /// assert_eq!(remainder, &['l']);
/// assert_eq!(chunks, &[['o', 'r'], ['e', 'm']]); /// assert_eq!(chunks, &[['o', 'r'], ['e', 'm']]);
/// ``` /// ```
#[unstable(feature = "slice_as_chunks", issue = "74985")] #[stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")]
#[inline] #[inline]
#[track_caller] #[track_caller]
#[must_use] #[must_use]
@@ -1424,6 +1461,18 @@ impl<T> [T] {
/// Splits the slice into a slice of `N`-element arrays, /// Splits the slice into a slice of `N`-element arrays,
/// assuming that there's no remainder. /// assuming that there's no remainder.
/// ///
/// This is the inverse operation to [`as_flattened_mut`].
///
/// [`as_flattened_mut`]: slice::as_flattened_mut
///
/// As this is `unsafe`, consider whether you could use [`as_chunks_mut`] or
/// [`as_rchunks_mut`] instead, perhaps via something like
/// `if let (chunks, []) = slice.as_chunks_mut()` or
/// `let (chunks, []) = slice.as_chunks_mut() else { unreachable!() };`.
///
/// [`as_chunks_mut`]: slice::as_chunks_mut
/// [`as_rchunks_mut`]: slice::as_rchunks_mut
///
/// # Safety /// # Safety
/// ///
/// This may only be called when /// This may only be called when
@@ -1433,7 +1482,6 @@ impl<T> [T] {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// #![feature(slice_as_chunks)]
/// let slice: &mut [char] = &mut ['l', 'o', 'r', 'e', 'm', '!']; /// let slice: &mut [char] = &mut ['l', 'o', 'r', 'e', 'm', '!'];
/// let chunks: &mut [[char; 1]] = /// let chunks: &mut [[char; 1]] =
/// // SAFETY: 1-element chunks never have remainder /// // SAFETY: 1-element chunks never have remainder
@@ -1450,7 +1498,8 @@ impl<T> [T] {
/// // let chunks: &[[_; 5]] = slice.as_chunks_unchecked_mut() // The slice length is not a multiple of 5 /// // let chunks: &[[_; 5]] = slice.as_chunks_unchecked_mut() // The slice length is not a multiple of 5
/// // let chunks: &[[_; 0]] = slice.as_chunks_unchecked_mut() // Zero-length chunks are never allowed /// // let chunks: &[[_; 0]] = slice.as_chunks_unchecked_mut() // Zero-length chunks are never allowed
/// ``` /// ```
#[unstable(feature = "slice_as_chunks", issue = "74985")] #[stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")]
#[inline] #[inline]
#[must_use] #[must_use]
pub const unsafe fn as_chunks_unchecked_mut<const N: usize>(&mut self) -> &mut [[T; N]] { pub const unsafe fn as_chunks_unchecked_mut<const N: usize>(&mut self) -> &mut [[T; N]] {
@@ -1470,15 +1519,27 @@ impl<T> [T] {
/// starting at the beginning of the slice, /// starting at the beginning of the slice,
/// and a remainder slice with length strictly less than `N`. /// and a remainder slice with length strictly less than `N`.
/// ///
/// The remainder is meaningful in the division sense. Given
/// `let (chunks, remainder) = slice.as_chunks_mut()`, then:
/// - `chunks.len()` equals `slice.len() / N`,
/// - `remainder.len()` equals `slice.len() % N`, and
/// - `slice.len()` equals `chunks.len() * N + remainder.len()`.
///
/// You can flatten the chunks back into a slice-of-`T` with [`as_flattened_mut`].
///
/// [`as_flattened_mut`]: slice::as_flattened_mut
///
/// # Panics /// # Panics
/// ///
/// Panics if `N` is zero. This check will most probably get changed to a compile time /// Panics if `N` is zero.
/// error before this method gets stabilized. ///
/// Note that this check is against a const generic parameter, not a runtime
/// value, and thus a particular monomorphization will either always panic
/// or it will never panic.
/// ///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// #![feature(slice_as_chunks)]
/// let v = &mut [0, 0, 0, 0, 0]; /// let v = &mut [0, 0, 0, 0, 0];
/// let mut count = 1; /// let mut count = 1;
/// ///
@@ -1490,7 +1551,8 @@ impl<T> [T] {
/// } /// }
/// assert_eq!(v, &[1, 1, 2, 2, 9]); /// assert_eq!(v, &[1, 1, 2, 2, 9]);
/// ``` /// ```
#[unstable(feature = "slice_as_chunks", issue = "74985")] #[stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")]
#[inline] #[inline]
#[track_caller] #[track_caller]
#[must_use] #[must_use]
@@ -1510,15 +1572,27 @@ impl<T> [T] {
/// starting at the end of the slice, /// starting at the end of the slice,
/// and a remainder slice with length strictly less than `N`. /// and a remainder slice with length strictly less than `N`.
/// ///
/// The remainder is meaningful in the division sense. Given
/// `let (remainder, chunks) = slice.as_rchunks_mut()`, then:
/// - `remainder.len()` equals `slice.len() % N`,
/// - `chunks.len()` equals `slice.len() / N`, and
/// - `slice.len()` equals `chunks.len() * N + remainder.len()`.
///
/// You can flatten the chunks back into a slice-of-`T` with [`as_flattened_mut`].
///
/// [`as_flattened_mut`]: slice::as_flattened_mut
///
/// # Panics /// # Panics
/// ///
/// Panics if `N` is zero. This check will most probably get changed to a compile time /// Panics if `N` is zero.
/// error before this method gets stabilized. ///
/// Note that this check is against a const generic parameter, not a runtime
/// value, and thus a particular monomorphization will either always panic
/// or it will never panic.
/// ///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// #![feature(slice_as_chunks)]
/// let v = &mut [0, 0, 0, 0, 0]; /// let v = &mut [0, 0, 0, 0, 0];
/// let mut count = 1; /// let mut count = 1;
/// ///
@@ -1530,7 +1604,8 @@ impl<T> [T] {
/// } /// }
/// assert_eq!(v, &[9, 1, 1, 2, 2]); /// assert_eq!(v, &[9, 1, 1, 2, 2]);
/// ``` /// ```
#[unstable(feature = "slice_as_chunks", issue = "74985")] #[stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")]
#[inline] #[inline]
#[track_caller] #[track_caller]
#[must_use] #[must_use]
@@ -4816,6 +4891,11 @@ impl<T> [MaybeUninit<T>] {
impl<T, const N: usize> [[T; N]] { impl<T, const N: usize> [[T; N]] {
/// Takes a `&[[T; N]]`, and flattens it to a `&[T]`. /// Takes a `&[[T; N]]`, and flattens it to a `&[T]`.
/// ///
/// For the opposite operation, see [`as_chunks`] and [`as_rchunks`].
///
/// [`as_chunks`]: slice::as_chunks
/// [`as_rchunks`]: slice::as_rchunks
///
/// # Panics /// # Panics
/// ///
/// This panics if the length of the resulting slice would overflow a `usize`. /// This panics if the length of the resulting slice would overflow a `usize`.
@@ -4856,6 +4936,11 @@ impl<T, const N: usize> [[T; N]] {
/// Takes a `&mut [[T; N]]`, and flattens it to a `&mut [T]`. /// Takes a `&mut [[T; N]]`, and flattens it to a `&mut [T]`.
/// ///
/// For the opposite operation, see [`as_chunks_mut`] and [`as_rchunks_mut`].
///
/// [`as_chunks_mut`]: slice::as_chunks_mut
/// [`as_rchunks_mut`]: slice::as_rchunks_mut
///
/// # Panics /// # Panics
/// ///
/// This panics if the length of the resulting slice would overflow a `usize`. /// This panics if the length of the resulting slice would overflow a `usize`.

View File

@@ -1,7 +1,6 @@
//@revisions: stack tree //@revisions: stack tree
//@[tree]compile-flags: -Zmiri-tree-borrows //@[tree]compile-flags: -Zmiri-tree-borrows
//@compile-flags: -Zmiri-strict-provenance //@compile-flags: -Zmiri-strict-provenance
#![feature(slice_as_chunks)]
#![feature(slice_partition_dedup)] #![feature(slice_partition_dedup)]
#![feature(layout_for_ptr)] #![feature(layout_for_ptr)]
@@ -227,7 +226,7 @@ fn test_for_invalidated_pointers() {
buffer.reverse(); buffer.reverse();
// Calls `fn as_chunks_unchecked_mut` internally (requires unstable `#![feature(slice_as_chunks)]`): // Calls `fn as_chunks_unchecked_mut` internally:
assert_eq!(2, buffer.as_chunks_mut::<32>().0.len()); assert_eq!(2, buffer.as_chunks_mut::<32>().0.len());
for chunk in buffer.as_chunks_mut::<32>().0 { for chunk in buffer.as_chunks_mut::<32>().0 {
for elem in chunk { for elem in chunk {

View File

@@ -2,7 +2,6 @@
//@ only-64bit (because the LLVM type of i64 for usize shows up) //@ only-64bit (because the LLVM type of i64 for usize shows up)
#![crate_type = "lib"] #![crate_type = "lib"]
#![feature(slice_as_chunks)]
// CHECK-LABEL: @chunks4 // CHECK-LABEL: @chunks4
#[no_mangle] #[no_mangle]