Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add [T]::as_chunks(_mut) #76635

Merged
merged 1 commit into from
Oct 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 4 additions & 15 deletions library/core/src/slice/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2102,13 +2102,8 @@ pub struct ArrayChunks<'a, T: 'a, const N: usize> {
impl<'a, T, const N: usize> ArrayChunks<'a, T, N> {
#[inline]
pub(super) fn new(slice: &'a [T]) -> Self {
let len = slice.len() / N;
let (fst, snd) = slice.split_at(len * N);
// SAFETY: We cast a slice of `len * N` elements into
// a slice of `len` many `N` elements chunks.
let array_slice: &[[T; N]] = unsafe { from_raw_parts(fst.as_ptr().cast(), len) };

Self { iter: array_slice.iter(), rem: snd }
let (array_slice, rem) = slice.as_chunks();
Self { iter: array_slice.iter(), rem }
}

/// Returns the remainder of the original slice that is not going to be
Expand Down Expand Up @@ -2229,14 +2224,8 @@ pub struct ArrayChunksMut<'a, T: 'a, const N: usize> {
impl<'a, T, const N: usize> ArrayChunksMut<'a, T, N> {
#[inline]
pub(super) fn new(slice: &'a mut [T]) -> Self {
let len = slice.len() / N;
let (fst, snd) = slice.split_at_mut(len * N);
// SAFETY: We cast a slice of `len * N` elements into
// a slice of `len` many `N` elements chunks.
unsafe {
let array_slice: &mut [[T; N]] = from_raw_parts_mut(fst.as_mut_ptr().cast(), len);
Self { iter: array_slice.iter_mut(), rem: snd }
}
let (array_slice, rem) = slice.as_chunks_mut();
Self { iter: array_slice.iter_mut(), rem }
}

/// Returns the remainder of the original slice that is not going to be
Expand Down
67 changes: 67 additions & 0 deletions library/core/src/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,36 @@ impl<T> [T] {
ChunksExactMut::new(self, chunk_size)
}

/// Splits the slice into a slice of `N`-element arrays,
/// starting at the beginning of the slice,
/// and a remainder slice with length strictly less than `N`.
///
/// # Panics
///
/// Panics if `N` is 0. This check will most probably get changed to a compile time
/// error before this method gets stabilized.
LukasKalbertodt marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Examples
///
/// ```
/// #![feature(slice_as_chunks)]
/// let slice = ['l', 'o', 'r', 'e', 'm'];
/// let (chunks, remainder) = slice.as_chunks();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the example be: let (...) = slice.as_chunks::<2>();? (Similar for as_chunks_mut.)

I imagine this example with implicit N only works because N is inferred from the subsequent assert_eq! structure. (But I'm pretty new to Rust, so maybe I'm missing something!)

Thanks!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does indeed infer N, and that's meant to be a good thing, but perhaps the example should have a comment pointing this out.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could either put slice.as_chunks() as comment or slice.as_chunks::<2>() as comment to show that it is being infered.

But in some cases type is not infered which may be confusing, like Some([]) cannot be infered as N for [T; N] so it may contradict to this comment here, so it would also be safer to say "most of the time it could be infered" for now. I recall seeing this in one of the pull request.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it does infer N in these cases. I agree it would be nice to also have some examples that turbofish it.

Maybe some like these, to emphasize the length math:

let slice = ['-'; 100];
let (chunks, remainder) = slice.as_chunks::<20>();
assert_eq!((chunks.len(), remainder.len()), (5, 0));
let (chunks, remainder) = slice.as_chunks::<30>();
assert_eq!((chunks.len(), remainder.len()), (3, 10));

/// assert_eq!(chunks, &[['l', 'o'], ['r', 'e']]);
/// assert_eq!(remainder, &['m']);
/// ```
#[unstable(feature = "slice_as_chunks", issue = "74985")]
#[inline]
pub fn as_chunks<const N: usize>(&self) -> (&[[T; N]], &[T]) {
assert_ne!(N, 0);
let len = self.len() / N;
let (multiple_of_n, remainder) = self.split_at(len * N);
// SAFETY: We cast a slice of `len * N` elements into
// a slice of `len` many `N` elements chunks.
let array_slice: &[[T; N]] = unsafe { from_raw_parts(multiple_of_n.as_ptr().cast(), len) };
(array_slice, remainder)
}

/// Returns an iterator over `N` elements of the slice at a time, starting at the
/// beginning of the slice.
///
Expand Down Expand Up @@ -917,6 +947,43 @@ impl<T> [T] {
ArrayChunks::new(self)
}

/// Splits the slice into a slice of `N`-element arrays,
/// starting at the beginning of the slice,
/// and a remainder slice with length strictly less than `N`.
///
/// # Panics
///
/// Panics if `N` is 0. This check will most probably get changed to a compile time
/// error before this method gets stabilized.
///
/// # Examples
///
/// ```
/// #![feature(slice_as_chunks)]
/// let v = &mut [0, 0, 0, 0, 0];
/// let mut count = 1;
///
/// let (chunks, remainder) = v.as_chunks_mut();
/// remainder[0] = 9;
/// for chunk in chunks {
/// *chunk = [count; 2];
/// count += 1;
/// }
/// assert_eq!(v, &[1, 1, 2, 2, 9]);
/// ```
#[unstable(feature = "slice_as_chunks", issue = "74985")]
#[inline]
pub fn as_chunks_mut<const N: usize>(&mut self) -> (&mut [[T; N]], &mut [T]) {
assert_ne!(N, 0);
let len = self.len() / N;
let (multiple_of_n, remainder) = self.split_at_mut(len * N);
let array_slice: &mut [[T; N]] =
// SAFETY: We cast a slice of `len * N` elements into
// a slice of `len` many `N` elements chunks.
unsafe { from_raw_parts_mut(multiple_of_n.as_mut_ptr().cast(), len) };
(array_slice, remainder)
}

/// Returns an iterator over `N` elements of the slice at a time, starting at the
/// beginning of the slice.
///
Expand Down