Skip to content

Commit 23a6777

Browse files
authored
Rollup merge of #75026 - JulianKnodt:array_windows, r=Amanieu
Add array_windows fn This mimicks the functionality added by array_chunks, and implements a const-generic form of `windows`. It makes egregious use of `unsafe`, but by necessity because the array must be re-interpreted as a slice of arrays, and unlike array_chunks this cannot be done by casting the original array once, since each time the index is advanced it needs to move one element, not `N`. I'm planning on adding more tests, but this should be good enough as a premise for the functionality. Notably: should there be more functions overwritten for the iterator implementation/in general? ~~I've marked the issue as #74985 as there is no corresponding exact issue for `array_windows`, but it's based of off `array_chunks`.~~ Edit: See Issue #75027 created by @lcnr for tracking issue ~~Do not merge until I add more tests, please.~~ r? @lcnr
2 parents 7bb106f + f240abc commit 23a6777

File tree

7 files changed

+196
-6
lines changed

7 files changed

+196
-6
lines changed

library/alloc/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
#![cfg_attr(test, feature(test))]
7777
#![feature(allocator_api)]
7878
#![feature(array_chunks)]
79+
#![feature(array_windows)]
7980
#![feature(allow_internal_unstable)]
8081
#![feature(arbitrary_self_types)]
8182
#![feature(box_patterns)]

library/alloc/src/slice.rs

+2
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ pub use core::slice::check_range;
9797
pub use core::slice::ArrayChunks;
9898
#[unstable(feature = "array_chunks", issue = "74985")]
9999
pub use core::slice::ArrayChunksMut;
100+
#[unstable(feature = "array_windows", issue = "75027")]
101+
pub use core::slice::ArrayWindows;
100102
#[stable(feature = "slice_get_slice", since = "1.28.0")]
101103
pub use core::slice::SliceIndex;
102104
#[stable(feature = "from_ref", since = "1.28.0")]

library/core/src/slice/iter.rs

+100
Original file line numberDiff line numberDiff line change
@@ -1687,6 +1687,106 @@ unsafe impl<'a, T> TrustedRandomAccess for ChunksExactMut<'a, T> {
16871687
}
16881688
}
16891689

1690+
/// A windowed iterator over a slice in overlapping chunks (`N` elements at a
1691+
/// time), starting at the beginning of the slice
1692+
///
1693+
/// This struct is created by the [`array_windows`] method on [slices].
1694+
///
1695+
/// [`array_windows`]: ../../std/primitive.slice.html#method.array_windows
1696+
/// [slices]: ../../std/primitive.slice.html
1697+
#[derive(Debug, Clone, Copy)]
1698+
#[unstable(feature = "array_windows", issue = "75027")]
1699+
pub struct ArrayWindows<'a, T: 'a, const N: usize> {
1700+
pub(crate) slice_head: *const T,
1701+
pub(crate) num: usize,
1702+
pub(crate) marker: marker::PhantomData<&'a [T; N]>,
1703+
}
1704+
1705+
#[unstable(feature = "array_windows", issue = "75027")]
1706+
impl<'a, T, const N: usize> Iterator for ArrayWindows<'a, T, N> {
1707+
type Item = &'a [T; N];
1708+
1709+
#[inline]
1710+
fn next(&mut self) -> Option<Self::Item> {
1711+
if self.num == 0 {
1712+
return None;
1713+
}
1714+
// SAFETY:
1715+
// This is safe because it's indexing into a slice guaranteed to be length > N.
1716+
let ret = unsafe { &*self.slice_head.cast::<[T; N]>() };
1717+
// SAFETY: Guaranteed that there are at least 1 item remaining otherwise
1718+
// earlier branch would've been hit
1719+
self.slice_head = unsafe { self.slice_head.add(1) };
1720+
1721+
self.num -= 1;
1722+
Some(ret)
1723+
}
1724+
1725+
#[inline]
1726+
fn size_hint(&self) -> (usize, Option<usize>) {
1727+
(self.num, Some(self.num))
1728+
}
1729+
1730+
#[inline]
1731+
fn count(self) -> usize {
1732+
self.num
1733+
}
1734+
1735+
#[inline]
1736+
fn nth(&mut self, n: usize) -> Option<Self::Item> {
1737+
if self.num <= n {
1738+
self.num = 0;
1739+
return None;
1740+
}
1741+
// SAFETY:
1742+
// This is safe because it's indexing into a slice guaranteed to be length > N.
1743+
let ret = unsafe { &*self.slice_head.add(n).cast::<[T; N]>() };
1744+
// SAFETY: Guaranteed that there are at least n items remaining
1745+
self.slice_head = unsafe { self.slice_head.add(n + 1) };
1746+
1747+
self.num -= n + 1;
1748+
Some(ret)
1749+
}
1750+
1751+
#[inline]
1752+
fn last(mut self) -> Option<Self::Item> {
1753+
self.nth(self.num.checked_sub(1)?)
1754+
}
1755+
}
1756+
1757+
#[unstable(feature = "array_windows", issue = "75027")]
1758+
impl<'a, T, const N: usize> DoubleEndedIterator for ArrayWindows<'a, T, N> {
1759+
#[inline]
1760+
fn next_back(&mut self) -> Option<&'a [T; N]> {
1761+
if self.num == 0 {
1762+
return None;
1763+
}
1764+
// SAFETY: Guaranteed that there are n items remaining, n-1 for 0-indexing.
1765+
let ret = unsafe { &*self.slice_head.add(self.num - 1).cast::<[T; N]>() };
1766+
self.num -= 1;
1767+
Some(ret)
1768+
}
1769+
1770+
#[inline]
1771+
fn nth_back(&mut self, n: usize) -> Option<&'a [T; N]> {
1772+
if self.num <= n {
1773+
self.num = 0;
1774+
return None;
1775+
}
1776+
// SAFETY: Guaranteed that there are n items remaining, n-1 for 0-indexing.
1777+
let ret = unsafe { &*self.slice_head.add(self.num - (n + 1)).cast::<[T; N]>() };
1778+
self.num -= n + 1;
1779+
Some(ret)
1780+
}
1781+
}
1782+
1783+
#[unstable(feature = "array_windows", issue = "75027")]
1784+
impl<T, const N: usize> ExactSizeIterator for ArrayWindows<'_, T, N> {
1785+
fn is_empty(&self) -> bool {
1786+
self.num == 0
1787+
}
1788+
}
1789+
16901790
/// An iterator over a slice in (non-overlapping) chunks (`N` elements at a
16911791
/// time), starting at the beginning of the slice.
16921792
///

library/core/src/slice/mod.rs

+37
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ pub use iter::{RChunks, RChunksExact, RChunksExactMut, RChunksMut};
5656
#[unstable(feature = "array_chunks", issue = "74985")]
5757
pub use iter::{ArrayChunks, ArrayChunksMut};
5858

59+
#[unstable(feature = "array_windows", issue = "75027")]
60+
pub use iter::ArrayWindows;
61+
5962
#[unstable(feature = "split_inclusive", issue = "72360")]
6063
pub use iter::{SplitInclusive, SplitInclusiveMut};
6164

@@ -1026,6 +1029,40 @@ impl<T> [T] {
10261029
}
10271030
}
10281031

1032+
/// Returns an iterator over overlapping windows of `N` elements of a slice,
1033+
/// starting at the beginning of the slice.
1034+
///
1035+
/// This is the const generic equivalent of [`windows`].
1036+
///
1037+
/// If `N` is smaller than the size of the array, it will return no windows.
1038+
///
1039+
/// # Panics
1040+
///
1041+
/// Panics if `N` is 0. This check will most probably get changed to a compile time
1042+
/// error before this method gets stabilized.
1043+
///
1044+
/// # Examples
1045+
///
1046+
/// ```
1047+
/// #![feature(array_windows)]
1048+
/// let slice = [0, 1, 2, 3];
1049+
/// let mut iter = slice.array_windows();
1050+
/// assert_eq!(iter.next().unwrap(), &[0, 1]);
1051+
/// assert_eq!(iter.next().unwrap(), &[1, 2]);
1052+
/// assert_eq!(iter.next().unwrap(), &[2, 3]);
1053+
/// assert!(iter.next().is_none());
1054+
/// ```
1055+
///
1056+
/// [`windows`]: #method.windows
1057+
#[unstable(feature = "array_windows", issue = "75027")]
1058+
#[inline]
1059+
pub fn array_windows<const N: usize>(&self) -> ArrayWindows<'_, T, N> {
1060+
assert_ne!(N, 0);
1061+
1062+
let num_windows = self.len().saturating_sub(N - 1);
1063+
ArrayWindows { slice_head: self.as_ptr(), num: num_windows, marker: marker::PhantomData }
1064+
}
1065+
10291066
/// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the end
10301067
/// of the slice.
10311068
///

library/core/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#![feature(array_chunks)]
33
#![feature(array_methods)]
44
#![feature(array_map)]
5+
#![feature(array_windows)]
56
#![feature(bool_to_option)]
67
#![feature(bound_cloned)]
78
#![feature(box_syntax)]

library/core/tests/slice.rs

+49
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,55 @@ fn test_array_chunks_mut_zip() {
657657
assert_eq!(v1, [13, 14, 19, 20, 4]);
658658
}
659659

660+
#[test]
661+
fn test_array_windows_infer() {
662+
let v: &[i32] = &[0, 1, 0, 1];
663+
assert_eq!(v.array_windows::<2>().count(), 3);
664+
let c = v.array_windows();
665+
for &[a, b] in c {
666+
assert_eq!(a + b, 1);
667+
}
668+
669+
let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6];
670+
let total = v2.array_windows().map(|&[a, b, c]| a + b + c).sum::<i32>();
671+
assert_eq!(total, 3 + 6 + 9 + 12 + 15);
672+
}
673+
674+
#[test]
675+
fn test_array_windows_count() {
676+
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
677+
let c = v.array_windows::<3>();
678+
assert_eq!(c.count(), 4);
679+
680+
let v2: &[i32] = &[0, 1, 2, 3, 4];
681+
let c2 = v2.array_windows::<6>();
682+
assert_eq!(c2.count(), 0);
683+
684+
let v3: &[i32] = &[];
685+
let c3 = v3.array_windows::<2>();
686+
assert_eq!(c3.count(), 0);
687+
}
688+
689+
#[test]
690+
fn test_array_windows_nth() {
691+
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
692+
let snd = v.array_windows::<4>().nth(1);
693+
assert_eq!(snd, Some(&[1, 2, 3, 4]));
694+
let mut arr_windows = v.array_windows::<2>();
695+
assert_ne!(arr_windows.nth(0), arr_windows.nth(0));
696+
let last = v.array_windows::<3>().last();
697+
assert_eq!(last, Some(&[3, 4, 5]));
698+
}
699+
700+
#[test]
701+
fn test_array_windows_nth_back() {
702+
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
703+
let snd = v.array_windows::<4>().nth_back(1);
704+
assert_eq!(snd, Some(&[1, 2, 3, 4]));
705+
let mut arr_windows = v.array_windows::<2>();
706+
assert_ne!(arr_windows.nth_back(0), arr_windows.nth_back(0));
707+
}
708+
660709
#[test]
661710
fn test_rchunks_count() {
662711
let v: &[i32] = &[0, 1, 2, 3, 4, 5];

src/test/ui/const-generics/type-dependent/issue-61936.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,21 @@
55
#![cfg_attr(min, feature(min_const_generics))]
66

77
trait SliceExt<T: Clone> {
8-
fn array_windows<'a, const N: usize>(&'a self) -> ArrayWindows<'a, T, N>;
8+
fn array_windows_example<'a, const N: usize>(&'a self) -> ArrayWindowsExample<'a, T, N>;
99
}
1010

1111
impl <T: Clone> SliceExt<T> for [T] {
12-
fn array_windows<'a, const N: usize>(&'a self) -> ArrayWindows<'a, T, N> {
13-
ArrayWindows{ idx: 0, slice: &self }
12+
fn array_windows_example<'a, const N: usize>(&'a self) -> ArrayWindowsExample<'a, T, N> {
13+
ArrayWindowsExample{ idx: 0, slice: &self }
1414
}
1515
}
1616

17-
struct ArrayWindows<'a, T, const N: usize> {
17+
struct ArrayWindowsExample<'a, T, const N: usize> {
1818
slice: &'a [T],
1919
idx: usize,
2020
}
2121

22-
impl <'a, T: Clone, const N: usize> Iterator for ArrayWindows<'a, T, N> {
22+
impl <'a, T: Clone, const N: usize> Iterator for ArrayWindowsExample<'a, T, N> {
2323
type Item = [T; N];
2424
fn next(&mut self) -> Option<Self::Item> {
2525
// Note: this is unsound for some `T` and not meant as an example
@@ -45,7 +45,7 @@ const FOUR: usize = 4;
4545
fn main() {
4646
let v: Vec<usize> = vec![0; 100];
4747

48-
for array in v.as_slice().array_windows::<FOUR>() {
48+
for array in v.as_slice().array_windows_example::<FOUR>() {
4949
assert_eq!(array, [0, 0, 0, 0])
5050
}
5151
}

0 commit comments

Comments
 (0)