about summary refs log tree commit diff
path: root/src/libcore
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-07-12 19:39:04 +0000
committerbors <bors@rust-lang.org>2018-07-12 19:39:04 +0000
commit64f7de92166f1f7d940575feff57ac33bc384550 (patch)
tree47d3bac0007dc1d99287165c751f9ce958862dbc /src/libcore
parent6cc42a4488d5dbc4c4109ed4a2f2ea81efa77f86 (diff)
parent903624fb8d845afac62b1fca2d9114c04ef35fea (diff)
downloadrust-64f7de92166f1f7d940575feff57ac33bc384550.tar.gz
rust-64f7de92166f1f7d940575feff57ac33bc384550.zip
Auto merge of #51339 - sdroege:exact-chunks-remainder, r=alexcrichton
Add ExactChunks::remainder and ExactChunks::into_remainder

These allow to get the leftover items of the slice that are not being
iterated as part of the iterator due to not filling a complete chunk.

The mutable version consumes the slice because otherwise we would either
a) have to borrow the iterator instead of taking the lifetime of
the underlying slice, which is not what *any* of the other iterator
functions is doing, or
b) would allow returning multiple mutable references to the same data

The current behaviour of consuming the iterator is consistent with
IterMut::into_slice for the normal iterator.

----

This is related to https://github.com/rust-lang/rust/issues/47115#issuecomment-392685177 and the following comments.

While there the discussion was first about a way to get the "tail" of the iterator (everything from the slice that is still not iterated yet), this gives kind of unintuitive behaviour and is inconsistent with how the other slice iterators work.

Unintuitive because the `next_back` would have no effect on the tail (or otherwise the tail could not include the remainder items), inconsistent because a) generally the idea of the slice iterators seems to be to only ever return items that were not iterated yet (and don't provide a way to access the same item twice) and b) we would return a "flat" `&[T]` slice but the iterator's shape is `&[[T]]` instead, c) the mutable variant would have to borrow from the iterator instead of the underlying slice (all other iterator functions borrow from the underlying slice!)

As such, I've only implemented functions to get the remainder. This also allows the implementation to be completely safe still (and around slices instead of raw pointers), while getting the tail would either be inefficient or would have to be implemented around raw pointers.

CC @kerollmops
Diffstat (limited to 'src/libcore')
-rw-r--r--src/libcore/slice/mod.rs48
-rw-r--r--src/libcore/tests/slice.rs14
2 files changed, 54 insertions, 8 deletions
diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs
index ed29d80cb90..f6ef4a79dc7 100644
--- a/src/libcore/slice/mod.rs
+++ b/src/libcore/slice/mod.rs
@@ -729,7 +729,8 @@ impl<T> [T] {
     /// Returns an iterator over `chunk_size` elements of the slice at a
     /// time. The chunks are slices and do not overlap. If `chunk_size` does
     /// not divide the length of the slice, then the last up to `chunk_size-1`
-    /// elements will be omitted.
+    /// elements will be omitted and can be retrieved from the `remainder`
+    /// function of the iterator.
     ///
     /// Due to each chunk having exactly `chunk_size` elements, the compiler
     /// can often optimize the resulting code better than in the case of
@@ -758,14 +759,15 @@ impl<T> [T] {
         assert!(chunk_size != 0);
         let rem = self.len() % chunk_size;
         let len = self.len() - rem;
-        ExactChunks { v: &self[..len], chunk_size: chunk_size}
+        let (fst, snd) = self.split_at(len);
+        ExactChunks { v: fst, rem: snd, chunk_size: chunk_size}
     }
 
     /// Returns an iterator over `chunk_size` elements of the slice at a time.
     /// The chunks are mutable slices, and do not overlap. If `chunk_size` does
     /// not divide the length of the slice, then the last up to `chunk_size-1`
-    /// elements will be omitted.
-    ///
+    /// elements will be omitted and can be retrieved from the `into_remainder`
+    /// function of the iterator.
     ///
     /// Due to each chunk having exactly `chunk_size` elements, the compiler
     /// can often optimize the resulting code better than in the case of
@@ -799,7 +801,8 @@ impl<T> [T] {
         assert!(chunk_size != 0);
         let rem = self.len() % chunk_size;
         let len = self.len() - rem;
-        ExactChunksMut { v: &mut self[..len], chunk_size: chunk_size}
+        let (fst, snd) = self.split_at_mut(len);
+        ExactChunksMut { v: fst, rem: snd, chunk_size: chunk_size}
     }
 
     /// Divides one slice into two at an index.
@@ -3657,25 +3660,39 @@ unsafe impl<'a, T> TrustedRandomAccess for ChunksMut<'a, T> {
 /// time).
 ///
 /// When the slice len is not evenly divided by the chunk size, the last
-/// up to `chunk_size-1` elements will be omitted.
+/// up to `chunk_size-1` elements will be omitted but can be retrieved from
+/// the [`remainder`] function from the iterator.
 ///
 /// This struct is created by the [`exact_chunks`] method on [slices].
 ///
 /// [`exact_chunks`]: ../../std/primitive.slice.html#method.exact_chunks
+/// [`remainder`]: ../../std/slice/struct.ExactChunks.html#method.remainder
 /// [slices]: ../../std/primitive.slice.html
 #[derive(Debug)]
 #[unstable(feature = "exact_chunks", issue = "47115")]
 pub struct ExactChunks<'a, T:'a> {
     v: &'a [T],
+    rem: &'a [T],
     chunk_size: usize
 }
 
+#[unstable(feature = "exact_chunks", issue = "47115")]
+impl<'a, T> ExactChunks<'a, T> {
+    /// Return the remainder of the original slice that is not going to be
+    /// returned by the iterator. The returned slice has at most `chunk_size-1`
+    /// elements.
+    pub fn remainder(&self) -> &'a [T] {
+        self.rem
+    }
+}
+
 // FIXME(#26925) Remove in favor of `#[derive(Clone)]`
 #[unstable(feature = "exact_chunks", issue = "47115")]
 impl<'a, T> Clone for ExactChunks<'a, T> {
     fn clone(&self) -> ExactChunks<'a, T> {
         ExactChunks {
             v: self.v,
+            rem: self.rem,
             chunk_size: self.chunk_size,
         }
     }
@@ -3763,21 +3780,36 @@ unsafe impl<'a, T> TrustedRandomAccess for ExactChunks<'a, T> {
 }
 
 /// An iterator over a slice in (non-overlapping) mutable chunks (`chunk_size`
-/// elements at a time). When the slice len is not evenly divided by the chunk
-/// size, the last up to `chunk_size-1` elements will be omitted.
+/// elements at a time).
+///
+/// When the slice len is not evenly divided by the chunk size, the last up to
+/// `chunk_size-1` elements will be omitted but can be retrieved from the
+/// [`into_remainder`] function from the iterator.
 ///
 /// This struct is created by the [`exact_chunks_mut`] method on [slices].
 ///
 /// [`exact_chunks_mut`]: ../../std/primitive.slice.html#method.exact_chunks_mut
+/// [`into_remainder`]: ../../std/slice/struct.ExactChunksMut.html#method.into_remainder
 /// [slices]: ../../std/primitive.slice.html
 #[derive(Debug)]
 #[unstable(feature = "exact_chunks", issue = "47115")]
 pub struct ExactChunksMut<'a, T:'a> {
     v: &'a mut [T],
+    rem: &'a mut [T],
     chunk_size: usize
 }
 
 #[unstable(feature = "exact_chunks", issue = "47115")]
+impl<'a, T> ExactChunksMut<'a, T> {
+    /// Return the remainder of the original slice that is not going to be
+    /// returned by the iterator. The returned slice has at most `chunk_size-1`
+    /// elements.
+    pub fn into_remainder(self) -> &'a mut [T] {
+        self.rem
+    }
+}
+
+#[unstable(feature = "exact_chunks", issue = "47115")]
 impl<'a, T> Iterator for ExactChunksMut<'a, T> {
     type Item = &'a mut [T];
 
diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs
index 7981567067d..2b37acdfe3e 100644
--- a/src/libcore/tests/slice.rs
+++ b/src/libcore/tests/slice.rs
@@ -260,6 +260,13 @@ fn test_exact_chunks_last() {
 }
 
 #[test]
+fn test_exact_chunks_remainder() {
+    let v: &[i32] = &[0, 1, 2, 3, 4];
+    let c = v.exact_chunks(2);
+    assert_eq!(c.remainder(), &[4]);
+}
+
+#[test]
 fn test_exact_chunks_zip() {
     let v1: &[i32] = &[0, 1, 2, 3, 4];
     let v2: &[i32] = &[6, 7, 8, 9, 10];
@@ -311,6 +318,13 @@ fn test_exact_chunks_mut_last() {
 }
 
 #[test]
+fn test_exact_chunks_mut_remainder() {
+    let v: &mut [i32] = &mut [0, 1, 2, 3, 4];
+    let c = v.exact_chunks_mut(2);
+    assert_eq!(c.into_remainder(), &[4]);
+}
+
+#[test]
 fn test_exact_chunks_mut_zip() {
     let v1: &mut [i32] = &mut [0, 1, 2, 3, 4];
     let v2: &[i32] = &[6, 7, 8, 9, 10];