]> git.lizzy.rs Git - rust.git/commitdiff
Rollup merge of #47126 - sdroege:exact-chunks, r=bluss
authorkennytm <kennytm@gmail.com>
Mon, 15 Jan 2018 08:55:28 +0000 (16:55 +0800)
committerkennytm <kennytm@gmail.com>
Mon, 15 Jan 2018 10:49:31 +0000 (18:49 +0800)
Add slice::ExactChunks and ::ExactChunksMut iterators

These guarantee that always the requested slice size will be returned
and any leftoever elements at the end will be ignored. It allows llvm to
get rid of bounds checks in the code using the iterator.

This is inspired by the same iterators provided by ndarray.

Fixes https://github.com/rust-lang/rust/issues/47115

I'll add unit tests for all this if the general idea and behaviour makes sense for everybody.
Also see https://github.com/rust-lang/rust/issues/47115#issuecomment-354715511 for an example what this improves.

1  2 
src/liballoc/slice.rs
src/liballoc/tests/slice.rs
src/libcore/slice/mod.rs
src/libcore/tests/slice.rs

diff --combined src/liballoc/slice.rs
index 2c7bdc427ea7c70dbe594ca55d82c215fe62606d,f8980b8777e93e3cfe2490c9b051c42904a66772..861f72bcf88ee42b2e5fcf711a9fc1a1a6c358f4
@@@ -123,6 -123,8 +123,8 @@@ pub use core::slice::{from_raw_parts, f
  pub use core::slice::{from_ref, from_ref_mut};
  #[unstable(feature = "slice_get_slice", issue = "35729")]
  pub use core::slice::SliceIndex;
+ #[unstable(feature = "exact_chunks", issue = "47115")]
+ pub use core::slice::{ExactChunks, ExactChunksMut};
  
  ////////////////////////////////////////////////////////////////////////////////
  // Basic slice extension methods
@@@ -611,6 -613,9 +613,9 @@@ impl<T> [T] 
      /// not divide the length of the slice, then the last chunk will
      /// not have length `chunk_size`.
      ///
+     /// See [`exact_chunks`] for a variant of this iterator that returns chunks
+     /// of always exactly `chunk_size` elements.
+     ///
      /// # Panics
      ///
      /// Panics if `chunk_size` is 0.
          core_slice::SliceExt::chunks(self, chunk_size)
      }
  
+     /// 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.
+     ///
+     /// Due to each chunk having exactly `chunk_size` elements, the compiler
+     /// can often optimize the resulting code better than in the case of
+     /// [`chunks`].
+     ///
+     /// # Panics
+     ///
+     /// Panics if `chunk_size` is 0.
+     ///
+     /// # Examples
+     ///
+     /// ```
+     /// #![feature(exact_chunks)]
+     ///
+     /// let slice = ['l', 'o', 'r', 'e', 'm'];
+     /// let mut iter = slice.exact_chunks(2);
+     /// assert_eq!(iter.next().unwrap(), &['l', 'o']);
+     /// assert_eq!(iter.next().unwrap(), &['r', 'e']);
+     /// assert!(iter.next().is_none());
+     /// ```
+     #[unstable(feature = "exact_chunks", issue = "47115")]
+     #[inline]
+     pub fn exact_chunks(&self, chunk_size: usize) -> ExactChunks<T> {
+         core_slice::SliceExt::exact_chunks(self, 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 chunk will not
      /// have length `chunk_size`.
      ///
+     /// See [`exact_chunks_mut`] for a variant of this iterator that returns chunks
+     /// of always exactly `chunk_size` elements.
+     ///
      /// # Panics
      ///
      /// Panics if `chunk_size` is 0.
          core_slice::SliceExt::chunks_mut(self, 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.
+     ///
+     ///
+     /// Due to each chunk having exactly `chunk_size` elements, the compiler
+     /// can often optimize the resulting code better than in the case of
+     /// [`chunks_mut`].
+     ///
+     /// # Panics
+     ///
+     /// Panics if `chunk_size` is 0.
+     ///
+     /// # Examples
+     ///
+     /// ```
+     /// #![feature(exact_chunks)]
+     ///
+     /// let v = &mut [0, 0, 0, 0, 0];
+     /// let mut count = 1;
+     ///
+     /// for chunk in v.exact_chunks_mut(2) {
+     ///     for elem in chunk.iter_mut() {
+     ///         *elem += count;
+     ///     }
+     ///     count += 1;
+     /// }
+     /// assert_eq!(v, &[1, 1, 2, 2, 0]);
+     /// ```
+     #[unstable(feature = "exact_chunks", issue = "47115")]
+     #[inline]
+     pub fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut<T> {
+         core_slice::SliceExt::exact_chunks_mut(self, chunk_size)
+     }
      /// Divides one slice into two at an index.
      ///
      /// The first will contain all indices from `[0, mid)` (excluding
          core_slice::SliceExt::sort_unstable_by_key(self, f);
      }
  
 -    /// Permutes the slice in-place such that `self[mid..]` moves to the
 -    /// beginning of the slice while `self[..mid]` moves to the end of the
 -    /// slice.  Equivalently, rotates the slice `mid` places to the left
 -    /// or `k = self.len() - mid` places to the right.
 +    /// Rotates the slice in-place such that the first `mid` elements of the
 +    /// slice move to the end while the last `self.len() - mid` elements move to
 +    /// the front. After calling `rotate_left`, the element previously at index
 +    /// `mid` will become the first element in the slice.
      ///
 -    /// This is a "k-rotation", a permutation in which item `i` moves to
 -    /// position `i + k`, modulo the length of the slice.  See _Elements
 -    /// of Programming_ [ยง10.4][eop].
 +    /// # Panics
 +    ///
 +    /// This function will panic if `mid` is greater than the length of the
 +    /// slice. Note that `mid == self.len()` does _not_ panic and is a no-op
 +    /// rotation.
 +    ///
 +    /// # Complexity
 +    ///
 +    /// Takes linear (in `self.len()`) time.
 +    ///
 +    /// # Examples
      ///
 -    /// Rotation by `mid` and rotation by `k` are inverse operations.
 +    /// ```
 +    /// #![feature(slice_rotate)]
      ///
 -    /// [eop]: https://books.google.com/books?id=CO9ULZGINlsC&pg=PA178&q=k-rotation
 +    /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
 +    /// a.rotate_left(2);
 +    /// assert_eq!(a, ['c', 'd', 'e', 'f', 'a', 'b']);
 +    /// ```
 +    ///
 +    /// Rotating a subslice:
 +    ///
 +    /// ```
 +    /// #![feature(slice_rotate)]
 +    ///
 +    /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
 +    /// a[1..5].rotate_left(1);
 +    /// assert_eq!(a, ['a', 'c', 'd', 'e', 'b', 'f']);
 +    /// ```
 +    #[unstable(feature = "slice_rotate", issue = "41891")]
 +    pub fn rotate_left(&mut self, mid: usize) {
 +        core_slice::SliceExt::rotate_left(self, mid);
 +    }
 +
 +    #[unstable(feature = "slice_rotate", issue = "41891")]
 +    #[rustc_deprecated(since = "", reason = "renamed to `rotate_left`")]
 +    pub fn rotate(&mut self, mid: usize) {
 +        core_slice::SliceExt::rotate_left(self, mid);
 +    }
 +
 +    /// Rotates the slice in-place such that the first `self.len() - k`
 +    /// elements of the slice move to the end while the last `k` elements move
 +    /// to the front. After calling `rotate_right`, the element previously at
 +    /// index `self.len() - k` will become the first element in the slice.
      ///
      /// # Panics
      ///
 -    /// This function will panic if `mid` is greater than the length of the
 -    /// slice.  (Note that `mid == self.len()` does _not_ panic; it's a nop
 -    /// rotation with `k == 0`, the inverse of a rotation with `mid == 0`.)
 +    /// This function will panic if `k` is greater than the length of the
 +    /// slice. Note that `k == self.len()` does _not_ panic and is a no-op
 +    /// rotation.
      ///
      /// # Complexity
      ///
      /// ```
      /// #![feature(slice_rotate)]
      ///
 -    /// let mut a = [1, 2, 3, 4, 5, 6, 7];
 -    /// let mid = 2;
 -    /// a.rotate(mid);
 -    /// assert_eq!(&a, &[3, 4, 5, 6, 7, 1, 2]);
 -    /// let k = a.len() - mid;
 -    /// a.rotate(k);
 -    /// assert_eq!(&a, &[1, 2, 3, 4, 5, 6, 7]);
 -    ///
 -    /// use std::ops::Range;
 -    /// fn slide<T>(slice: &mut [T], range: Range<usize>, to: usize) {
 -    ///     if to < range.start {
 -    ///         slice[to..range.end].rotate(range.start-to);
 -    ///     } else if to > range.end {
 -    ///         slice[range.start..to].rotate(range.end-range.start);
 -    ///     }
 -    /// }
 -    /// let mut v: Vec<_> = (0..10).collect();
 -    /// slide(&mut v, 1..4, 7);
 -    /// assert_eq!(&v, &[0, 4, 5, 6, 1, 2, 3, 7, 8, 9]);
 -    /// slide(&mut v, 6..8, 1);
 -    /// assert_eq!(&v, &[0, 3, 7, 4, 5, 6, 1, 2, 8, 9]);
 +    /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
 +    /// a.rotate_right(2);
 +    /// assert_eq!(a, ['e', 'f', 'a', 'b', 'c', 'd']);
 +    /// ```
 +    ///
 +    /// Rotate a subslice:
 +    ///
 +    /// ```
 +    /// #![feature(slice_rotate)]
 +    ///
 +    /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
 +    /// a[1..5].rotate_right(1);
 +    /// assert_eq!(a, ['a', 'e', 'b', 'c', 'd', 'f']);
      /// ```
      #[unstable(feature = "slice_rotate", issue = "41891")]
 -    pub fn rotate(&mut self, mid: usize) {
 -        core_slice::SliceExt::rotate(self, mid);
 +    pub fn rotate_right(&mut self, k: usize) {
 +        core_slice::SliceExt::rotate_right(self, k);
      }
  
      /// Copies the elements from `src` into `self`.
      /// let x = s.to_vec();
      /// // Here, `s` and `x` can be modified independently.
      /// ```
 +    #[rustc_conversion_suggestion]
      #[stable(feature = "rust1", since = "1.0.0")]
      #[inline]
      pub fn to_vec(&self) -> Vec<T>
index 49bdc9e1b90de5c252ac2321e9d0e85da3643899,5a3e5e6cb780da916cb73f72556d2f8c0bbd2c8d..1a9d26fd1a29150b5c18f950d49f0b5deb70aa4b
@@@ -494,72 -494,37 +494,72 @@@ fn test_sort_stability() 
  }
  
  #[test]
 -fn test_rotate() {
 +fn test_rotate_left() {
      let expected: Vec<_> = (0..13).collect();
      let mut v = Vec::new();
  
      // no-ops
      v.clone_from(&expected);
 -    v.rotate(0);
 +    v.rotate_left(0);
      assert_eq!(v, expected);
 -    v.rotate(expected.len());
 +    v.rotate_left(expected.len());
      assert_eq!(v, expected);
      let mut zst_array = [(), (), ()];
 -    zst_array.rotate(2);
 +    zst_array.rotate_left(2);
  
      // happy path
      v = (5..13).chain(0..5).collect();
 -    v.rotate(8);
 +    v.rotate_left(8);
      assert_eq!(v, expected);
  
      let expected: Vec<_> = (0..1000).collect();
  
      // small rotations in large slice, uses ptr::copy
      v = (2..1000).chain(0..2).collect();
 -    v.rotate(998);
 +    v.rotate_left(998);
      assert_eq!(v, expected);
      v = (998..1000).chain(0..998).collect();
 -    v.rotate(2);
 +    v.rotate_left(2);
      assert_eq!(v, expected);
  
      // non-small prime rotation, has a few rounds of swapping
      v = (389..1000).chain(0..389).collect();
 -    v.rotate(1000-389);
 +    v.rotate_left(1000-389);
 +    assert_eq!(v, expected);
 +}
 +
 +#[test]
 +fn test_rotate_right() {
 +    let expected: Vec<_> = (0..13).collect();
 +    let mut v = Vec::new();
 +
 +    // no-ops
 +    v.clone_from(&expected);
 +    v.rotate_right(0);
 +    assert_eq!(v, expected);
 +    v.rotate_right(expected.len());
 +    assert_eq!(v, expected);
 +    let mut zst_array = [(), (), ()];
 +    zst_array.rotate_right(2);
 +
 +    // happy path
 +    v = (5..13).chain(0..5).collect();
 +    v.rotate_right(5);
 +    assert_eq!(v, expected);
 +
 +    let expected: Vec<_> = (0..1000).collect();
 +
 +    // small rotations in large slice, uses ptr::copy
 +    v = (2..1000).chain(0..2).collect();
 +    v.rotate_right(2);
 +    assert_eq!(v, expected);
 +    v = (998..1000).chain(0..998).collect();
 +    v.rotate_right(998);
 +    assert_eq!(v, expected);
 +
 +    // non-small prime rotation, has a few rounds of swapping
 +    v = (389..1000).chain(0..389).collect();
 +    v.rotate_right(389);
      assert_eq!(v, expected);
  }
  
@@@ -945,6 -910,30 +945,30 @@@ fn test_chunksator_0() 
      let _it = v.chunks(0);
  }
  
+ #[test]
+ fn test_exact_chunksator() {
+     let v = &[1, 2, 3, 4, 5];
+     assert_eq!(v.exact_chunks(2).len(), 2);
+     let chunks: &[&[_]] = &[&[1, 2], &[3, 4]];
+     assert_eq!(v.exact_chunks(2).collect::<Vec<_>>(), chunks);
+     let chunks: &[&[_]] = &[&[1, 2, 3]];
+     assert_eq!(v.exact_chunks(3).collect::<Vec<_>>(), chunks);
+     let chunks: &[&[_]] = &[];
+     assert_eq!(v.exact_chunks(6).collect::<Vec<_>>(), chunks);
+     let chunks: &[&[_]] = &[&[3, 4], &[1, 2]];
+     assert_eq!(v.exact_chunks(2).rev().collect::<Vec<_>>(), chunks);
+ }
+ #[test]
+ #[should_panic]
+ fn test_exact_chunksator_0() {
+     let v = &[1, 2, 3, 4];
+     let _it = v.exact_chunks(0);
+ }
  #[test]
  fn test_reverse_part() {
      let mut values = [1, 2, 3, 4, 5];
@@@ -1159,7 -1148,7 +1183,7 @@@ fn test_mut_chunks() 
          }
      }
      let result = [0, 0, 0, 1, 1, 1, 2];
-     assert!(v == result);
+     assert_eq!(v, result);
  }
  
  #[test]
@@@ -1171,7 -1160,7 +1195,7 @@@ fn test_mut_chunks_rev() 
          }
      }
      let result = [2, 2, 2, 1, 1, 1, 0];
-     assert!(v == result);
+     assert_eq!(v, result);
  }
  
  #[test]
@@@ -1181,6 -1170,38 +1205,38 @@@ fn test_mut_chunks_0() 
      let _it = v.chunks_mut(0);
  }
  
+ #[test]
+ fn test_mut_exact_chunks() {
+     let mut v = [0, 1, 2, 3, 4, 5, 6];
+     assert_eq!(v.exact_chunks_mut(2).len(), 3);
+     for (i, chunk) in v.exact_chunks_mut(3).enumerate() {
+         for x in chunk {
+             *x = i as u8;
+         }
+     }
+     let result = [0, 0, 0, 1, 1, 1, 6];
+     assert_eq!(v, result);
+ }
+ #[test]
+ fn test_mut_exact_chunks_rev() {
+     let mut v = [0, 1, 2, 3, 4, 5, 6];
+     for (i, chunk) in v.exact_chunks_mut(3).rev().enumerate() {
+         for x in chunk {
+             *x = i as u8;
+         }
+     }
+     let result = [1, 1, 1, 0, 0, 0, 6];
+     assert_eq!(v, result);
+ }
+ #[test]
+ #[should_panic]
+ fn test_mut_exact_chunks_0() {
+     let mut v = [1, 2, 3, 4];
+     let _it = v.exact_chunks_mut(0);
+ }
  #[test]
  fn test_mut_last() {
      let mut x = [1, 2, 3, 4, 5];
diff --combined src/libcore/slice/mod.rs
index e6b79314aa96d47a2dd19800b1ce5731be430282,9379a064a6c11a6fa59d0ba4e897dfe5517bfc4e..48e82666d35151cafa566bf965cddd874cb8b283
@@@ -104,6 -104,9 +104,9 @@@ pub trait SliceExt 
      #[stable(feature = "core", since = "1.6.0")]
      fn chunks(&self, size: usize) -> Chunks<Self::Item>;
  
+     #[unstable(feature = "exact_chunks", issue = "47115")]
+     fn exact_chunks(&self, size: usize) -> ExactChunks<Self::Item>;
      #[stable(feature = "core", since = "1.6.0")]
      fn get<I>(&self, index: I) -> Option<&I::Output>
          where I: SliceIndex<Self>;
      #[stable(feature = "core", since = "1.6.0")]
      fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<Self::Item>;
  
+     #[unstable(feature = "exact_chunks", issue = "47115")]
+     fn exact_chunks_mut(&mut self, size: usize) -> ExactChunksMut<Self::Item>;
      #[stable(feature = "core", since = "1.6.0")]
      fn swap(&mut self, a: usize, b: usize);
  
      fn ends_with(&self, needle: &[Self::Item]) -> bool where Self::Item: PartialEq;
  
      #[unstable(feature = "slice_rotate", issue = "41891")]
 -    fn rotate(&mut self, mid: usize);
 +    fn rotate_left(&mut self, mid: usize);
 +
 +    #[unstable(feature = "slice_rotate", issue = "41891")]
 +    fn rotate_right(&mut self, k: usize);
  
      #[stable(feature = "clone_from_slice", since = "1.7.0")]
      fn clone_from_slice(&mut self, src: &[Self::Item]) where Self::Item: Clone;
@@@ -356,6 -359,14 +362,14 @@@ impl<T> SliceExt for [T] 
          Chunks { v: self, chunk_size: chunk_size }
      }
  
+     #[inline]
+     fn exact_chunks(&self, chunk_size: usize) -> ExactChunks<T> {
+         assert!(chunk_size != 0);
+         let rem = self.len() % chunk_size;
+         let len = self.len() - rem;
+         ExactChunks { v: &self[..len], chunk_size: chunk_size}
+     }
      #[inline]
      fn get<I>(&self, index: I) -> Option<&I::Output>
          where I: SliceIndex<[T]>
          ChunksMut { v: self, chunk_size: chunk_size }
      }
  
+     #[inline]
+     fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut<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}
+     }
      #[inline]
      fn swap(&mut self, a: usize, b: usize) {
          unsafe {
          self.binary_search_by(|p| p.cmp(x))
      }
  
 -    fn rotate(&mut self, mid: usize) {
 +    fn rotate_left(&mut self, mid: usize) {
          assert!(mid <= self.len());
          let k = self.len() - mid;
  
          }
      }
  
 +    fn rotate_right(&mut self, k: usize) {
 +        assert!(k <= self.len());
 +        let mid = self.len() - k;
 +
 +        unsafe {
 +            let p = self.as_mut_ptr();
 +            rotate::ptr_rotate(mid, p.offset(mid as isize), k);
 +        }
 +    }
 +
      #[inline]
      fn clone_from_slice(&mut self, src: &[T]) where T: Clone {
          assert!(self.len() == src.len(),
@@@ -2378,6 -2387,209 +2400,209 @@@ unsafe impl<'a, T> TrustedRandomAccess 
      fn may_have_side_effect() -> bool { false }
  }
  
+ /// An iterator over a slice in (non-overlapping) 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.
+ ///
+ /// This struct is created by the [`exact_chunks`] method on [slices].
+ ///
+ /// [`exact_chunks`]: ../../std/primitive.slice.html#method.exact_chunks
+ /// [slices]: ../../std/primitive.slice.html
+ #[derive(Debug)]
+ #[unstable(feature = "exact_chunks", issue = "47115")]
+ pub struct ExactChunks<'a, T:'a> {
+     v: &'a [T],
+     chunk_size: usize
+ }
+ // 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,
+             chunk_size: self.chunk_size,
+         }
+     }
+ }
+ #[unstable(feature = "exact_chunks", issue = "47115")]
+ impl<'a, T> Iterator for ExactChunks<'a, T> {
+     type Item = &'a [T];
+     #[inline]
+     fn next(&mut self) -> Option<&'a [T]> {
+         if self.v.len() < self.chunk_size {
+             None
+         } else {
+             let (fst, snd) = self.v.split_at(self.chunk_size);
+             self.v = snd;
+             Some(fst)
+         }
+     }
+     #[inline]
+     fn size_hint(&self) -> (usize, Option<usize>) {
+         let n = self.v.len() / self.chunk_size;
+         (n, Some(n))
+     }
+     #[inline]
+     fn count(self) -> usize {
+         self.len()
+     }
+     #[inline]
+     fn nth(&mut self, n: usize) -> Option<Self::Item> {
+         let (start, overflow) = n.overflowing_mul(self.chunk_size);
+         if start >= self.v.len() || overflow {
+             self.v = &[];
+             None
+         } else {
+             let (_, snd) = self.v.split_at(start);
+             self.v = snd;
+             self.next()
+         }
+     }
+     #[inline]
+     fn last(mut self) -> Option<Self::Item> {
+         self.next_back()
+     }
+ }
+ #[unstable(feature = "exact_chunks", issue = "47115")]
+ impl<'a, T> DoubleEndedIterator for ExactChunks<'a, T> {
+     #[inline]
+     fn next_back(&mut self) -> Option<&'a [T]> {
+         if self.v.len() < self.chunk_size {
+             None
+         } else {
+             let (fst, snd) = self.v.split_at(self.v.len() - self.chunk_size);
+             self.v = fst;
+             Some(snd)
+         }
+     }
+ }
+ #[unstable(feature = "exact_chunks", issue = "47115")]
+ impl<'a, T> ExactSizeIterator for ExactChunks<'a, T> {
+     fn is_empty(&self) -> bool {
+         self.v.is_empty()
+     }
+ }
+ #[unstable(feature = "fused", issue = "35602")]
+ impl<'a, T> FusedIterator for ExactChunks<'a, T> {}
+ #[doc(hidden)]
+ unsafe impl<'a, T> TrustedRandomAccess for ExactChunks<'a, T> {
+     unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T] {
+         let start = i * self.chunk_size;
+         from_raw_parts(self.v.as_ptr().offset(start as isize), self.chunk_size)
+     }
+     fn may_have_side_effect() -> bool { false }
+ }
+ /// 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.
+ ///
+ /// This struct is created by the [`exact_chunks_mut`] method on [slices].
+ ///
+ /// [`exact_chunks_mut`]: ../../std/primitive.slice.html#method.exact_chunks_mut
+ /// [slices]: ../../std/primitive.slice.html
+ #[derive(Debug)]
+ #[unstable(feature = "exact_chunks", issue = "47115")]
+ pub struct ExactChunksMut<'a, T:'a> {
+     v: &'a mut [T],
+     chunk_size: usize
+ }
+ #[unstable(feature = "exact_chunks", issue = "47115")]
+ impl<'a, T> Iterator for ExactChunksMut<'a, T> {
+     type Item = &'a mut [T];
+     #[inline]
+     fn next(&mut self) -> Option<&'a mut [T]> {
+         if self.v.len() < self.chunk_size {
+             None
+         } else {
+             let tmp = mem::replace(&mut self.v, &mut []);
+             let (head, tail) = tmp.split_at_mut(self.chunk_size);
+             self.v = tail;
+             Some(head)
+         }
+     }
+     #[inline]
+     fn size_hint(&self) -> (usize, Option<usize>) {
+         let n = self.v.len() / self.chunk_size;
+         (n, Some(n))
+     }
+     #[inline]
+     fn count(self) -> usize {
+         self.len()
+     }
+     #[inline]
+     fn nth(&mut self, n: usize) -> Option<&'a mut [T]> {
+         let (start, overflow) = n.overflowing_mul(self.chunk_size);
+         if start >= self.v.len() || overflow {
+             self.v = &mut [];
+             None
+         } else {
+             let tmp = mem::replace(&mut self.v, &mut []);
+             let (_, snd) = tmp.split_at_mut(start);
+             self.v = snd;
+             self.next()
+         }
+     }
+     #[inline]
+     fn last(mut self) -> Option<Self::Item> {
+         self.next_back()
+     }
+ }
+ #[unstable(feature = "exact_chunks", issue = "47115")]
+ impl<'a, T> DoubleEndedIterator for ExactChunksMut<'a, T> {
+     #[inline]
+     fn next_back(&mut self) -> Option<&'a mut [T]> {
+         if self.v.len() < self.chunk_size {
+             None
+         } else {
+             let tmp = mem::replace(&mut self.v, &mut []);
+             let tmp_len = tmp.len();
+             let (head, tail) = tmp.split_at_mut(tmp_len - self.chunk_size);
+             self.v = head;
+             Some(tail)
+         }
+     }
+ }
+ #[unstable(feature = "exact_chunks", issue = "47115")]
+ impl<'a, T> ExactSizeIterator for ExactChunksMut<'a, T> {
+     fn is_empty(&self) -> bool {
+         self.v.is_empty()
+     }
+ }
+ #[unstable(feature = "fused", issue = "35602")]
+ impl<'a, T> FusedIterator for ExactChunksMut<'a, T> {}
+ #[doc(hidden)]
+ unsafe impl<'a, T> TrustedRandomAccess for ExactChunksMut<'a, T> {
+     unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut [T] {
+         let start = i * self.chunk_size;
+         from_raw_parts_mut(self.v.as_mut_ptr().offset(start as isize), self.chunk_size)
+     }
+     fn may_have_side_effect() -> bool { false }
+ }
  //
  // Free functions
  //
index 40e5fe5758ac987e629444ee0a612d15c3f183d7,a89a88ac0af1e05498781d7da22d4b2c40d47b08..f7a4a71e5cfda4909700cd455c5b762d6a3965cd
@@@ -117,12 -117,12 +117,12 @@@ fn test_chunks_count() 
  fn test_chunks_nth() {
      let v: &[i32] = &[0, 1, 2, 3, 4, 5];
      let mut c = v.chunks(2);
-     assert_eq!(c.nth(1).unwrap()[1], 3);
-     assert_eq!(c.next().unwrap()[0], 4);
+     assert_eq!(c.nth(1).unwrap(), &[2, 3]);
+     assert_eq!(c.next().unwrap(), &[4, 5]);
  
      let v2: &[i32] = &[0, 1, 2, 3, 4];
      let mut c2 = v2.chunks(3);
-     assert_eq!(c2.nth(1).unwrap()[1], 4);
+     assert_eq!(c2.nth(1).unwrap(), &[3, 4]);
      assert_eq!(c2.next(), None);
  }
  
@@@ -168,12 -168,12 +168,12 @@@ fn test_chunks_mut_count() 
  fn test_chunks_mut_nth() {
      let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
      let mut c = v.chunks_mut(2);
-     assert_eq!(c.nth(1).unwrap()[1], 3);
-     assert_eq!(c.next().unwrap()[0], 4);
+     assert_eq!(c.nth(1).unwrap(), &[2, 3]);
+     assert_eq!(c.next().unwrap(), &[4, 5]);
  
      let v2: &mut [i32] = &mut [0, 1, 2, 3, 4];
      let mut c2 = v2.chunks_mut(3);
-     assert_eq!(c2.nth(1).unwrap()[1], 4);
+     assert_eq!(c2.nth(1).unwrap(), &[3, 4]);
      assert_eq!(c2.next(), None);
  }
  
  fn test_chunks_mut_last() {
      let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
      let c = v.chunks_mut(2);
-     assert_eq!(c.last().unwrap()[1], 5);
+     assert_eq!(c.last().unwrap(), &[4, 5]);
  
      let v2: &mut [i32] = &mut [0, 1, 2, 3, 4];
      let c2 = v2.chunks_mut(2);
-     assert_eq!(c2.last().unwrap()[0], 4);
+     assert_eq!(c2.last().unwrap(), &[4]);
  }
  
  #[test]
@@@ -202,6 -202,110 +202,110 @@@ fn test_chunks_mut_zip() 
      assert_eq!(v1, [13, 14, 19, 20, 14]);
  }
  
+ #[test]
+ fn test_exact_chunks_count() {
+     let v: &[i32] = &[0, 1, 2, 3, 4, 5];
+     let c = v.exact_chunks(3);
+     assert_eq!(c.count(), 2);
+     let v2: &[i32] = &[0, 1, 2, 3, 4];
+     let c2 = v2.exact_chunks(2);
+     assert_eq!(c2.count(), 2);
+     let v3: &[i32] = &[];
+     let c3 = v3.exact_chunks(2);
+     assert_eq!(c3.count(), 0);
+ }
+ #[test]
+ fn test_exact_chunks_nth() {
+     let v: &[i32] = &[0, 1, 2, 3, 4, 5];
+     let mut c = v.exact_chunks(2);
+     assert_eq!(c.nth(1).unwrap(), &[2, 3]);
+     assert_eq!(c.next().unwrap(), &[4, 5]);
+     let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6];
+     let mut c2 = v2.exact_chunks(3);
+     assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]);
+     assert_eq!(c2.next(), None);
+ }
+ #[test]
+ fn test_exact_chunks_last() {
+     let v: &[i32] = &[0, 1, 2, 3, 4, 5];
+     let c = v.exact_chunks(2);
+     assert_eq!(c.last().unwrap(), &[4, 5]);
+     let v2: &[i32] = &[0, 1, 2, 3, 4];
+     let c2 = v2.exact_chunks(2);
+     assert_eq!(c2.last().unwrap(), &[2, 3]);
+ }
+ #[test]
+ fn test_exact_chunks_zip() {
+     let v1: &[i32] = &[0, 1, 2, 3, 4];
+     let v2: &[i32] = &[6, 7, 8, 9, 10];
+     let res = v1.exact_chunks(2)
+         .zip(v2.exact_chunks(2))
+         .map(|(a, b)| a.iter().sum::<i32>() + b.iter().sum::<i32>())
+         .collect::<Vec<_>>();
+     assert_eq!(res, vec![14, 22]);
+ }
+ #[test]
+ fn test_exact_chunks_mut_count() {
+     let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
+     let c = v.exact_chunks_mut(3);
+     assert_eq!(c.count(), 2);
+     let v2: &mut [i32] = &mut [0, 1, 2, 3, 4];
+     let c2 = v2.exact_chunks_mut(2);
+     assert_eq!(c2.count(), 2);
+     let v3: &mut [i32] = &mut [];
+     let c3 = v3.exact_chunks_mut(2);
+     assert_eq!(c3.count(), 0);
+ }
+ #[test]
+ fn test_exact_chunks_mut_nth() {
+     let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
+     let mut c = v.exact_chunks_mut(2);
+     assert_eq!(c.nth(1).unwrap(), &[2, 3]);
+     assert_eq!(c.next().unwrap(), &[4, 5]);
+     let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6];
+     let mut c2 = v2.exact_chunks_mut(3);
+     assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]);
+     assert_eq!(c2.next(), None);
+ }
+ #[test]
+ fn test_exact_chunks_mut_last() {
+     let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
+     let c = v.exact_chunks_mut(2);
+     assert_eq!(c.last().unwrap(), &[4, 5]);
+     let v2: &mut [i32] = &mut [0, 1, 2, 3, 4];
+     let c2 = v2.exact_chunks_mut(2);
+     assert_eq!(c2.last().unwrap(), &[2, 3]);
+ }
+ #[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];
+     for (a, b) in v1.exact_chunks_mut(2).zip(v2.exact_chunks(2)) {
+         let sum = b.iter().sum::<i32>();
+         for v in a {
+             *v += sum;
+         }
+     }
+     assert_eq!(v1, [13, 14, 19, 20, 4]);
+ }
  #[test]
  fn test_windows_count() {
      let v: &[i32] = &[0, 1, 2, 3, 4, 5];
@@@ -329,32 -433,17 +433,32 @@@ fn test_iter_folds() 
  }
  
  #[test]
 -fn test_rotate() {
 +fn test_rotate_left() {
      const N: usize = 600;
      let a: &mut [_] = &mut [0; N];
      for i in 0..N {
          a[i] = i;
      }
  
 -    a.rotate(42);
 +    a.rotate_left(42);
      let k = N - 42;
  
      for i in 0..N {
 -        assert_eq!(a[(i+k)%N], i);
 +        assert_eq!(a[(i + k) % N], i);
 +    }
 +}
 +
 +#[test]
 +fn test_rotate_right() {
 +    const N: usize = 600;
 +    let a: &mut [_] = &mut [0; N];
 +    for i in 0..N {
 +        a[i] = i;
 +    }
 +
 +    a.rotate_right(42);
 +
 +    for i in 0..N {
 +        assert_eq!(a[(i + 42) % N], i);
      }
  }