Skip to content

Commit b33e234

Browse files
committed
Auto merge of #79895 - Kerollmops:slice-group-by, r=m-ou-se
The return of the GroupBy and GroupByMut iterators on slice According to rust-lang/rfcs#2477 (comment), I am opening this PR again, this time I implemented it in safe Rust only, it is therefore much easier to read and is completely safe. This PR proposes to add two new methods to the slice, the `group_by` and `group_by_mut`. These two methods provide a way to iterate over non-overlapping sub-slices of a base slice that are separated by the predicate given by the user (e.g. `Partial::eq`, `|a, b| a.abs() < b.abs()`). ```rust let slice = &[1, 1, 1, 3, 3, 2, 2, 2]; let mut iter = slice.group_by(|a, b| a == b); assert_eq!(iter.next(), Some(&[1, 1, 1][..])); assert_eq!(iter.next(), Some(&[3, 3][..])); assert_eq!(iter.next(), Some(&[2, 2, 2][..])); assert_eq!(iter.next(), None); ``` [An RFC](rust-lang/rfcs#2477) was open 2 years ago but wasn't necessary.
2 parents a6bd524 + 8b53be6 commit b33e234

File tree

7 files changed

+330
-0
lines changed

7 files changed

+330
-0
lines changed

library/alloc/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
#![feature(try_trait)]
140140
#![feature(type_alias_impl_trait)]
141141
#![feature(associated_type_bounds)]
142+
#![feature(slice_group_by)]
142143
// Allow testing this library
143144

144145
#[cfg(test)]

library/alloc/src/slice.rs

+2
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ pub use core::slice::{Chunks, Windows};
110110
pub use core::slice::{ChunksExact, ChunksExactMut};
111111
#[stable(feature = "rust1", since = "1.0.0")]
112112
pub use core::slice::{ChunksMut, Split, SplitMut};
113+
#[unstable(feature = "slice_group_by", issue = "80552")]
114+
pub use core::slice::{GroupBy, GroupByMut};
113115
#[stable(feature = "rust1", since = "1.0.0")]
114116
pub use core::slice::{Iter, IterMut};
115117
#[stable(feature = "rchunks", since = "1.31.0")]

library/alloc/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#![feature(iter_map_while)]
2121
#![feature(int_bits_const)]
2222
#![feature(vecdeque_binary_search)]
23+
#![feature(slice_group_by)]
2324

2425
use std::collections::hash_map::DefaultHasher;
2526
use std::hash::{Hash, Hasher};

library/alloc/tests/slice.rs

+58
Original file line numberDiff line numberDiff line change
@@ -1898,3 +1898,61 @@ fn subslice_patterns() {
18981898
m!(&mut v, [..] => ());
18991899
m!(&mut v, [x, .., y] => c!((x, y), (&mut N, &mut N), (&mut N(0), &mut N(4))));
19001900
}
1901+
1902+
#[test]
1903+
fn test_group_by() {
1904+
let slice = &[1, 1, 1, 3, 3, 2, 2, 2, 1, 0];
1905+
1906+
let mut iter = slice.group_by(|a, b| a == b);
1907+
assert_eq!(iter.next(), Some(&[1, 1, 1][..]));
1908+
assert_eq!(iter.next(), Some(&[3, 3][..]));
1909+
assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
1910+
assert_eq!(iter.next(), Some(&[1][..]));
1911+
assert_eq!(iter.next(), Some(&[0][..]));
1912+
assert_eq!(iter.next(), None);
1913+
1914+
let mut iter = slice.group_by(|a, b| a == b);
1915+
assert_eq!(iter.next_back(), Some(&[0][..]));
1916+
assert_eq!(iter.next_back(), Some(&[1][..]));
1917+
assert_eq!(iter.next_back(), Some(&[2, 2, 2][..]));
1918+
assert_eq!(iter.next_back(), Some(&[3, 3][..]));
1919+
assert_eq!(iter.next_back(), Some(&[1, 1, 1][..]));
1920+
assert_eq!(iter.next_back(), None);
1921+
1922+
let mut iter = slice.group_by(|a, b| a == b);
1923+
assert_eq!(iter.next(), Some(&[1, 1, 1][..]));
1924+
assert_eq!(iter.next_back(), Some(&[0][..]));
1925+
assert_eq!(iter.next(), Some(&[3, 3][..]));
1926+
assert_eq!(iter.next_back(), Some(&[1][..]));
1927+
assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
1928+
assert_eq!(iter.next_back(), None);
1929+
}
1930+
1931+
#[test]
1932+
fn test_group_by_mut() {
1933+
let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2, 1, 0];
1934+
1935+
let mut iter = slice.group_by_mut(|a, b| a == b);
1936+
assert_eq!(iter.next(), Some(&mut [1, 1, 1][..]));
1937+
assert_eq!(iter.next(), Some(&mut [3, 3][..]));
1938+
assert_eq!(iter.next(), Some(&mut [2, 2, 2][..]));
1939+
assert_eq!(iter.next(), Some(&mut [1][..]));
1940+
assert_eq!(iter.next(), Some(&mut [0][..]));
1941+
assert_eq!(iter.next(), None);
1942+
1943+
let mut iter = slice.group_by_mut(|a, b| a == b);
1944+
assert_eq!(iter.next_back(), Some(&mut [0][..]));
1945+
assert_eq!(iter.next_back(), Some(&mut [1][..]));
1946+
assert_eq!(iter.next_back(), Some(&mut [2, 2, 2][..]));
1947+
assert_eq!(iter.next_back(), Some(&mut [3, 3][..]));
1948+
assert_eq!(iter.next_back(), Some(&mut [1, 1, 1][..]));
1949+
assert_eq!(iter.next_back(), None);
1950+
1951+
let mut iter = slice.group_by_mut(|a, b| a == b);
1952+
assert_eq!(iter.next(), Some(&mut [1, 1, 1][..]));
1953+
assert_eq!(iter.next_back(), Some(&mut [0][..]));
1954+
assert_eq!(iter.next(), Some(&mut [3, 3][..]));
1955+
assert_eq!(iter.next_back(), Some(&mut [1][..]));
1956+
assert_eq!(iter.next(), Some(&mut [2, 2, 2][..]));
1957+
assert_eq!(iter.next_back(), None);
1958+
}

library/core/src/slice/iter.rs

+174
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// ignore-tidy-filelength
12
//! Definitions of a bunch of iterators for `[T]`.
23
34
#[macro_use] // import iterator! and forward_iterator!
@@ -2967,3 +2968,176 @@ unsafe impl<'a, T> TrustedRandomAccess for IterMut<'a, T> {
29672968
false
29682969
}
29692970
}
2971+
2972+
/// An iterator over slice in (non-overlapping) chunks separated by a predicate.
2973+
///
2974+
/// This struct is created by the [`group_by`] method on [slices].
2975+
///
2976+
/// [`group_by`]: ../../std/primitive.slice.html#method.group_by
2977+
/// [slices]: ../../std/primitive.slice.html
2978+
#[unstable(feature = "slice_group_by", issue = "80552")]
2979+
pub struct GroupBy<'a, T: 'a, P> {
2980+
slice: &'a [T],
2981+
predicate: P,
2982+
}
2983+
2984+
#[unstable(feature = "slice_group_by", issue = "80552")]
2985+
impl<'a, T: 'a, P> GroupBy<'a, T, P> {
2986+
pub(super) fn new(slice: &'a [T], predicate: P) -> Self {
2987+
GroupBy { slice, predicate }
2988+
}
2989+
}
2990+
2991+
#[unstable(feature = "slice_group_by", issue = "80552")]
2992+
impl<'a, T: 'a, P> Iterator for GroupBy<'a, T, P>
2993+
where
2994+
P: FnMut(&T, &T) -> bool,
2995+
{
2996+
type Item = &'a [T];
2997+
2998+
#[inline]
2999+
fn next(&mut self) -> Option<Self::Item> {
3000+
if self.slice.is_empty() {
3001+
None
3002+
} else {
3003+
let mut len = 1;
3004+
let mut iter = self.slice.windows(2);
3005+
while let Some([l, r]) = iter.next() {
3006+
if (self.predicate)(l, r) { len += 1 } else { break }
3007+
}
3008+
let (head, tail) = self.slice.split_at(len);
3009+
self.slice = tail;
3010+
Some(head)
3011+
}
3012+
}
3013+
3014+
#[inline]
3015+
fn size_hint(&self) -> (usize, Option<usize>) {
3016+
if self.slice.is_empty() { (0, Some(0)) } else { (1, Some(self.slice.len())) }
3017+
}
3018+
3019+
#[inline]
3020+
fn last(mut self) -> Option<Self::Item> {
3021+
self.next_back()
3022+
}
3023+
}
3024+
3025+
#[unstable(feature = "slice_group_by", issue = "80552")]
3026+
impl<'a, T: 'a, P> DoubleEndedIterator for GroupBy<'a, T, P>
3027+
where
3028+
P: FnMut(&T, &T) -> bool,
3029+
{
3030+
#[inline]
3031+
fn next_back(&mut self) -> Option<Self::Item> {
3032+
if self.slice.is_empty() {
3033+
None
3034+
} else {
3035+
let mut len = 1;
3036+
let mut iter = self.slice.windows(2);
3037+
while let Some([l, r]) = iter.next_back() {
3038+
if (self.predicate)(l, r) { len += 1 } else { break }
3039+
}
3040+
let (head, tail) = self.slice.split_at(self.slice.len() - len);
3041+
self.slice = head;
3042+
Some(tail)
3043+
}
3044+
}
3045+
}
3046+
3047+
#[unstable(feature = "slice_group_by", issue = "80552")]
3048+
impl<'a, T: 'a, P> FusedIterator for GroupBy<'a, T, P> where P: FnMut(&T, &T) -> bool {}
3049+
3050+
#[unstable(feature = "slice_group_by", issue = "80552")]
3051+
impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for GroupBy<'a, T, P> {
3052+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3053+
f.debug_struct("GroupBy").field("slice", &self.slice).finish()
3054+
}
3055+
}
3056+
3057+
/// An iterator over slice in (non-overlapping) mutable chunks separated
3058+
/// by a predicate.
3059+
///
3060+
/// This struct is created by the [`group_by_mut`] method on [slices].
3061+
///
3062+
/// [`group_by_mut`]: ../../std/primitive.slice.html#method.group_by_mut
3063+
/// [slices]: ../../std/primitive.slice.html
3064+
#[unstable(feature = "slice_group_by", issue = "80552")]
3065+
pub struct GroupByMut<'a, T: 'a, P> {
3066+
slice: &'a mut [T],
3067+
predicate: P,
3068+
}
3069+
3070+
#[unstable(feature = "slice_group_by", issue = "80552")]
3071+
impl<'a, T: 'a, P> GroupByMut<'a, T, P> {
3072+
pub(super) fn new(slice: &'a mut [T], predicate: P) -> Self {
3073+
GroupByMut { slice, predicate }
3074+
}
3075+
}
3076+
3077+
#[unstable(feature = "slice_group_by", issue = "80552")]
3078+
impl<'a, T: 'a, P> Iterator for GroupByMut<'a, T, P>
3079+
where
3080+
P: FnMut(&T, &T) -> bool,
3081+
{
3082+
type Item = &'a mut [T];
3083+
3084+
#[inline]
3085+
fn next(&mut self) -> Option<Self::Item> {
3086+
if self.slice.is_empty() {
3087+
None
3088+
} else {
3089+
let mut len = 1;
3090+
let mut iter = self.slice.windows(2);
3091+
while let Some([l, r]) = iter.next() {
3092+
if (self.predicate)(l, r) { len += 1 } else { break }
3093+
}
3094+
let slice = mem::take(&mut self.slice);
3095+
let (head, tail) = slice.split_at_mut(len);
3096+
self.slice = tail;
3097+
Some(head)
3098+
}
3099+
}
3100+
3101+
#[inline]
3102+
fn size_hint(&self) -> (usize, Option<usize>) {
3103+
if self.slice.is_empty() { (0, Some(0)) } else { (1, Some(self.slice.len())) }
3104+
}
3105+
3106+
#[inline]
3107+
fn last(mut self) -> Option<Self::Item> {
3108+
self.next_back()
3109+
}
3110+
}
3111+
3112+
#[unstable(feature = "slice_group_by", issue = "80552")]
3113+
impl<'a, T: 'a, P> DoubleEndedIterator for GroupByMut<'a, T, P>
3114+
where
3115+
P: FnMut(&T, &T) -> bool,
3116+
{
3117+
#[inline]
3118+
fn next_back(&mut self) -> Option<Self::Item> {
3119+
if self.slice.is_empty() {
3120+
None
3121+
} else {
3122+
let mut len = 1;
3123+
let mut iter = self.slice.windows(2);
3124+
while let Some([l, r]) = iter.next_back() {
3125+
if (self.predicate)(l, r) { len += 1 } else { break }
3126+
}
3127+
let slice = mem::take(&mut self.slice);
3128+
let (head, tail) = slice.split_at_mut(slice.len() - len);
3129+
self.slice = head;
3130+
Some(tail)
3131+
}
3132+
}
3133+
}
3134+
3135+
#[unstable(feature = "slice_group_by", issue = "80552")]
3136+
impl<'a, T: 'a, P> FusedIterator for GroupByMut<'a, T, P> where P: FnMut(&T, &T) -> bool {}
3137+
3138+
#[unstable(feature = "slice_group_by", issue = "80552")]
3139+
impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for GroupByMut<'a, T, P> {
3140+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3141+
f.debug_struct("GroupByMut").field("slice", &self.slice).finish()
3142+
}
3143+
}

library/core/src/slice/mod.rs

+93
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ pub use iter::{ArrayChunks, ArrayChunksMut};
5757
#[unstable(feature = "array_windows", issue = "75027")]
5858
pub use iter::ArrayWindows;
5959

60+
#[unstable(feature = "slice_group_by", issue = "80552")]
61+
pub use iter::{GroupBy, GroupByMut};
62+
6063
#[unstable(feature = "split_inclusive", issue = "72360")]
6164
pub use iter::{SplitInclusive, SplitInclusiveMut};
6265

@@ -1208,6 +1211,96 @@ impl<T> [T] {
12081211
RChunksExactMut::new(self, chunk_size)
12091212
}
12101213

1214+
/// Returns an iterator over the slice producing non-overlapping runs
1215+
/// of elements using the predicate to separate them.
1216+
///
1217+
/// The predicate is called on two elements following themselves,
1218+
/// it means the predicate is called on `slice[0]` and `slice[1]`
1219+
/// then on `slice[1]` and `slice[2]` and so on.
1220+
///
1221+
/// # Examples
1222+
///
1223+
/// ```
1224+
/// #![feature(slice_group_by)]
1225+
///
1226+
/// let slice = &[1, 1, 1, 3, 3, 2, 2, 2];
1227+
///
1228+
/// let mut iter = slice.group_by(|a, b| a == b);
1229+
///
1230+
/// assert_eq!(iter.next(), Some(&[1, 1, 1][..]));
1231+
/// assert_eq!(iter.next(), Some(&[3, 3][..]));
1232+
/// assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
1233+
/// assert_eq!(iter.next(), None);
1234+
/// ```
1235+
///
1236+
/// This method can be used to extract the sorted subslices:
1237+
///
1238+
/// ```
1239+
/// #![feature(slice_group_by)]
1240+
///
1241+
/// let slice = &[1, 1, 2, 3, 2, 3, 2, 3, 4];
1242+
///
1243+
/// let mut iter = slice.group_by(|a, b| a <= b);
1244+
///
1245+
/// assert_eq!(iter.next(), Some(&[1, 1, 2, 3][..]));
1246+
/// assert_eq!(iter.next(), Some(&[2, 3][..]));
1247+
/// assert_eq!(iter.next(), Some(&[2, 3, 4][..]));
1248+
/// assert_eq!(iter.next(), None);
1249+
/// ```
1250+
#[unstable(feature = "slice_group_by", issue = "80552")]
1251+
#[inline]
1252+
pub fn group_by<F>(&self, pred: F) -> GroupBy<'_, T, F>
1253+
where
1254+
F: FnMut(&T, &T) -> bool,
1255+
{
1256+
GroupBy::new(self, pred)
1257+
}
1258+
1259+
/// Returns an iterator over the slice producing non-overlapping mutable
1260+
/// runs of elements using the predicate to separate them.
1261+
///
1262+
/// The predicate is called on two elements following themselves,
1263+
/// it means the predicate is called on `slice[0]` and `slice[1]`
1264+
/// then on `slice[1]` and `slice[2]` and so on.
1265+
///
1266+
/// # Examples
1267+
///
1268+
/// ```
1269+
/// #![feature(slice_group_by)]
1270+
///
1271+
/// let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2];
1272+
///
1273+
/// let mut iter = slice.group_by_mut(|a, b| a == b);
1274+
///
1275+
/// assert_eq!(iter.next(), Some(&mut [1, 1, 1][..]));
1276+
/// assert_eq!(iter.next(), Some(&mut [3, 3][..]));
1277+
/// assert_eq!(iter.next(), Some(&mut [2, 2, 2][..]));
1278+
/// assert_eq!(iter.next(), None);
1279+
/// ```
1280+
///
1281+
/// This method can be used to extract the sorted subslices:
1282+
///
1283+
/// ```
1284+
/// #![feature(slice_group_by)]
1285+
///
1286+
/// let slice = &mut [1, 1, 2, 3, 2, 3, 2, 3, 4];
1287+
///
1288+
/// let mut iter = slice.group_by_mut(|a, b| a <= b);
1289+
///
1290+
/// assert_eq!(iter.next(), Some(&mut [1, 1, 2, 3][..]));
1291+
/// assert_eq!(iter.next(), Some(&mut [2, 3][..]));
1292+
/// assert_eq!(iter.next(), Some(&mut [2, 3, 4][..]));
1293+
/// assert_eq!(iter.next(), None);
1294+
/// ```
1295+
#[unstable(feature = "slice_group_by", issue = "80552")]
1296+
#[inline]
1297+
pub fn group_by_mut<F>(&mut self, pred: F) -> GroupByMut<'_, T, F>
1298+
where
1299+
F: FnMut(&T, &T) -> bool,
1300+
{
1301+
GroupByMut::new(self, pred)
1302+
}
1303+
12111304
/// Divides one slice into two at an index.
12121305
///
12131306
/// The first will contain all indices from `[0, mid)` (excluding

library/core/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
#![feature(nonzero_leading_trailing_zeros)]
7373
#![feature(const_option)]
7474
#![feature(integer_atomics)]
75+
#![feature(slice_group_by)]
7576
#![deny(unsafe_op_in_unsafe_fn)]
7677

7778
extern crate test;

0 commit comments

Comments
 (0)