Skip to content

Commit 7bb2c00

Browse files
Make <[T]>::array_* methods fail to compile on 0 len arrays
Methods updated: * `array_windows` * `array_chunks` * `array_chunks_mut` * `as_chunks` * `as_chunks_mut` * `as_chunks_unchecked` * `as_rchunks` * `as_rchunks_mut` * `as_chunks_unchecked_mut` I implemented this using compile time assertions. Example compilation error: ``` > rustc +stage1 .\improper_array_windows.rs --crate-type=rlib error[E0080]: evaluation of `core::slice::<impl [u32]>::array_windows::<0>::{constant#0}` failed --> J:\rust_lang\library\core\src\slice\mod.rs:1381:13 | 1381 | assert!(N != 0, "window size must be non-zero"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'window size must be non-zero', J:\rust_lang\library\core\src\slice\mod.rs:1381:13 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) note: the above error was encountered while instantiating `fn core::slice::<impl [u32]>::array_windows::<0>` --> .\improper_array_windows.rs:4:14 | 4 | for _ in s.array_windows::<0>(){ | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error ``` I also added doctests and adjusted documentation. Relates: #74985 #75027
1 parent 25f084d commit 7bb2c00

File tree

1 file changed

+121
-47
lines changed

1 file changed

+121
-47
lines changed

library/core/src/slice/mod.rs

+121-47
Original file line numberDiff line numberDiff line change
@@ -972,7 +972,6 @@ impl<T> [T] {
972972
///
973973
/// This may only be called when
974974
/// - The slice splits exactly into `N`-element chunks (aka `self.len() % N == 0`).
975-
/// - `N != 0`.
976975
///
977976
/// # Examples
978977
///
@@ -990,14 +989,28 @@ impl<T> [T] {
990989
///
991990
/// // These would be unsound:
992991
/// // let chunks: &[[_; 5]] = slice.as_chunks_unchecked() // The slice length is not a multiple of 5
993-
/// // let chunks: &[[_; 0]] = slice.as_chunks_unchecked() // Zero-length chunks are never allowed
994992
/// ```
993+
///
994+
/// It doesn't compile if chunk size is zero:
995+
/// ```compile_fail
996+
/// #![feature(slice_as_chunks)]
997+
/// const N: usize = 0;
998+
/// let slice = [1, 2, 3, 4];
999+
/// let _ = unsafe{
1000+
/// slice.as_chunks_unchecked::<N>()
1001+
/// };
1002+
/// ```
1003+
///
9951004
#[unstable(feature = "slice_as_chunks", issue = "74985")]
9961005
#[inline]
9971006
#[must_use]
9981007
pub const unsafe fn as_chunks_unchecked<const N: usize>(&self) -> &[[T; N]] {
1008+
const {
1009+
assert!(N != 0, "chunk size must be non-zero");
1010+
}
9991011
let this = self;
1000-
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
1012+
// SAFETY: Caller must guarantee that `N` exactly divides the slice length
1013+
// `N` cannot be zero because we checked it using compile assert above.
10011014
let new_len = unsafe {
10021015
assert_unsafe_precondition!(
10031016
"slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks",
@@ -1014,11 +1027,6 @@ impl<T> [T] {
10141027
/// starting at the beginning of the slice,
10151028
/// and a remainder slice with length strictly less than `N`.
10161029
///
1017-
/// # Panics
1018-
///
1019-
/// Panics if `N` is 0. This check will most probably get changed to a compile time
1020-
/// error before this method gets stabilized.
1021-
///
10221030
/// # Examples
10231031
///
10241032
/// ```
@@ -1039,16 +1047,28 @@ impl<T> [T] {
10391047
/// };
10401048
/// assert_eq!(chunks, &[['R', 'u'], ['s', 't']]);
10411049
/// ```
1050+
///
1051+
/// It doesn't compile if chunk size is zero:
1052+
/// ```compile_fail
1053+
/// #![feature(slice_as_chunks)]
1054+
/// const N: usize = 0;
1055+
/// let slice = [1, 2, 3, 4];
1056+
/// let _ = slice.as_chunks::<N>();
1057+
/// ```
1058+
///
10421059
#[unstable(feature = "slice_as_chunks", issue = "74985")]
10431060
#[inline]
10441061
#[track_caller]
10451062
#[must_use]
10461063
pub const fn as_chunks<const N: usize>(&self) -> (&[[T; N]], &[T]) {
1047-
assert!(N != 0, "chunk size must be non-zero");
1064+
const {
1065+
assert!(N != 0, "chunk size must be non-zero");
1066+
}
10481067
let len = self.len() / N;
10491068
let (multiple_of_n, remainder) = self.split_at(len * N);
1050-
// SAFETY: We already panicked for zero, and ensured by construction
1069+
// SAFETY: It is ensured by construction
10511070
// that the length of the subslice is a multiple of N.
1071+
// `N` cannot be zero because we checked it using compile assert above.
10521072
let array_slice = unsafe { multiple_of_n.as_chunks_unchecked() };
10531073
(array_slice, remainder)
10541074
}
@@ -1071,16 +1091,28 @@ impl<T> [T] {
10711091
/// assert_eq!(remainder, &['l']);
10721092
/// assert_eq!(chunks, &[['o', 'r'], ['e', 'm']]);
10731093
/// ```
1094+
///
1095+
/// It doesn't compile if chunk size is zero:
1096+
/// ```compile_fail
1097+
/// #![feature(slice_as_chunks)]
1098+
/// const N: usize = 0;
1099+
/// let slice = [1, 2, 3, 4];
1100+
/// let _ = slice.as_rchunks::<N>();
1101+
/// ```
1102+
///
10741103
#[unstable(feature = "slice_as_chunks", issue = "74985")]
10751104
#[inline]
10761105
#[track_caller]
10771106
#[must_use]
10781107
pub const fn as_rchunks<const N: usize>(&self) -> (&[T], &[[T; N]]) {
1079-
assert!(N != 0, "chunk size must be non-zero");
1108+
const {
1109+
assert!(N != 0, "chunk size must be non-zero");
1110+
}
10801111
let len = self.len() / N;
10811112
let (remainder, multiple_of_n) = self.split_at(self.len() - len * N);
1082-
// SAFETY: We already panicked for zero, and ensured by construction
1113+
// SAFETY: It is ensured by construction
10831114
// that the length of the subslice is a multiple of N.
1115+
// `N` cannot be zero because we checked it using compile assert above.
10841116
let array_slice = unsafe { multiple_of_n.as_chunks_unchecked() };
10851117
(remainder, array_slice)
10861118
}
@@ -1094,11 +1126,6 @@ impl<T> [T] {
10941126
///
10951127
/// This method is the const generic equivalent of [`chunks_exact`].
10961128
///
1097-
/// # Panics
1098-
///
1099-
/// Panics if `N` is 0. This check will most probably get changed to a compile time
1100-
/// error before this method gets stabilized.
1101-
///
11021129
/// # Examples
11031130
///
11041131
/// ```
@@ -1111,12 +1138,22 @@ impl<T> [T] {
11111138
/// assert_eq!(iter.remainder(), &['m']);
11121139
/// ```
11131140
///
1141+
/// It doesn't compile if chunk size is zero:
1142+
/// ```compile_fail
1143+
/// #![feature(array_chunks)]
1144+
/// const N: usize = 0;
1145+
/// let slice = [1, 2, 3, 4];
1146+
/// let _ = slice.array_chunks::<N>();
1147+
/// ```
1148+
///
11141149
/// [`chunks_exact`]: slice::chunks_exact
11151150
#[unstable(feature = "array_chunks", issue = "74985")]
11161151
#[inline]
11171152
#[track_caller]
11181153
pub fn array_chunks<const N: usize>(&self) -> ArrayChunks<'_, T, N> {
1119-
assert!(N != 0, "chunk size must be non-zero");
1154+
const {
1155+
assert!(N != 0, "chunk size must be non-zero");
1156+
}
11201157
ArrayChunks::new(self)
11211158
}
11221159

@@ -1127,7 +1164,6 @@ impl<T> [T] {
11271164
///
11281165
/// This may only be called when
11291166
/// - The slice splits exactly into `N`-element chunks (aka `self.len() % N == 0`).
1130-
/// - `N != 0`.
11311167
///
11321168
/// # Examples
11331169
///
@@ -1147,14 +1183,28 @@ impl<T> [T] {
11471183
///
11481184
/// // These would be unsound:
11491185
/// // let chunks: &[[_; 5]] = slice.as_chunks_unchecked_mut() // The slice length is not a multiple of 5
1150-
/// // let chunks: &[[_; 0]] = slice.as_chunks_unchecked_mut() // Zero-length chunks are never allowed
11511186
/// ```
1187+
///
1188+
/// It doesn't compile if chunk size is zero:
1189+
/// ```compile_fail
1190+
/// #![feature(slice_as_chunks)]
1191+
/// const N: usize = 0;
1192+
/// let mut slice = [1, 2, 3, 4];
1193+
/// let _ = unsafe{
1194+
/// slice.as_chunks_unchecked_mut::<N>();
1195+
/// };
1196+
/// ```
1197+
///
11521198
#[unstable(feature = "slice_as_chunks", issue = "74985")]
11531199
#[inline]
11541200
#[must_use]
11551201
pub const unsafe fn as_chunks_unchecked_mut<const N: usize>(&mut self) -> &mut [[T; N]] {
1202+
const {
1203+
assert!(N != 0, "chunk size must be non-zero");
1204+
}
11561205
let this = &*self;
1157-
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
1206+
// SAFETY: Caller must guarantee that `N` exactly divides the slice length
1207+
// `N` cannot be zero because we checked it using compile assert above.
11581208
let new_len = unsafe {
11591209
assert_unsafe_precondition!(
11601210
"slice::as_chunks_unchecked_mut requires `N != 0` and the slice to split exactly into `N`-element chunks",
@@ -1171,11 +1221,6 @@ impl<T> [T] {
11711221
/// starting at the beginning of the slice,
11721222
/// and a remainder slice with length strictly less than `N`.
11731223
///
1174-
/// # Panics
1175-
///
1176-
/// Panics if `N` is 0. This check will most probably get changed to a compile time
1177-
/// error before this method gets stabilized.
1178-
///
11791224
/// # Examples
11801225
///
11811226
/// ```
@@ -1191,16 +1236,28 @@ impl<T> [T] {
11911236
/// }
11921237
/// assert_eq!(v, &[1, 1, 2, 2, 9]);
11931238
/// ```
1239+
///
1240+
/// It doesn't compile if chunk size is zero:
1241+
/// ```compile_fail
1242+
/// #![feature(slice_as_chunks)]
1243+
/// const N: usize = 0;
1244+
/// let mut slice = [1, 2, 3, 4];
1245+
/// let _ = slice.as_chunks_mut::<N>();
1246+
/// ```
1247+
///
11941248
#[unstable(feature = "slice_as_chunks", issue = "74985")]
11951249
#[inline]
11961250
#[track_caller]
11971251
#[must_use]
11981252
pub const fn as_chunks_mut<const N: usize>(&mut self) -> (&mut [[T; N]], &mut [T]) {
1199-
assert!(N != 0, "chunk size must be non-zero");
1253+
const {
1254+
assert!(N != 0, "chunk size must be non-zero");
1255+
}
12001256
let len = self.len() / N;
12011257
let (multiple_of_n, remainder) = self.split_at_mut(len * N);
1202-
// SAFETY: We already panicked for zero, and ensured by construction
1258+
// SAFETY: It is ensured by construction
12031259
// that the length of the subslice is a multiple of N.
1260+
// `N` cannot be zero because we checked it using compile assert above.
12041261
let array_slice = unsafe { multiple_of_n.as_chunks_unchecked_mut() };
12051262
(array_slice, remainder)
12061263
}
@@ -1209,11 +1266,6 @@ impl<T> [T] {
12091266
/// starting at the end of the slice,
12101267
/// and a remainder slice with length strictly less than `N`.
12111268
///
1212-
/// # Panics
1213-
///
1214-
/// Panics if `N` is 0. This check will most probably get changed to a compile time
1215-
/// error before this method gets stabilized.
1216-
///
12171269
/// # Examples
12181270
///
12191271
/// ```
@@ -1229,16 +1281,28 @@ impl<T> [T] {
12291281
/// }
12301282
/// assert_eq!(v, &[9, 1, 1, 2, 2]);
12311283
/// ```
1284+
///
1285+
/// It doesn't compile if chunk size is zero:
1286+
/// ```compile_fail
1287+
/// #![feature(slice_as_chunks)]
1288+
/// const N: usize = 0;
1289+
/// let mut slice = [1, 2, 3, 4];
1290+
/// let _ = slice.as_rchunks_mut::<N>();
1291+
/// ```
1292+
///
12321293
#[unstable(feature = "slice_as_chunks", issue = "74985")]
12331294
#[inline]
12341295
#[track_caller]
12351296
#[must_use]
12361297
pub const fn as_rchunks_mut<const N: usize>(&mut self) -> (&mut [T], &mut [[T; N]]) {
1237-
assert!(N != 0, "chunk size must be non-zero");
1298+
const {
1299+
assert!(N != 0, "chunk size must be non-zero");
1300+
}
12381301
let len = self.len() / N;
12391302
let (remainder, multiple_of_n) = self.split_at_mut(self.len() - len * N);
1240-
// SAFETY: We already panicked for zero, and ensured by construction
1303+
// SAFETY: It is ensured by construction
12411304
// that the length of the subslice is a multiple of N.
1305+
// `N` cannot be zero because we checked it using compile assert above.
12421306
let array_slice = unsafe { multiple_of_n.as_chunks_unchecked_mut() };
12431307
(remainder, array_slice)
12441308
}
@@ -1252,11 +1316,6 @@ impl<T> [T] {
12521316
///
12531317
/// This method is the const generic equivalent of [`chunks_exact_mut`].
12541318
///
1255-
/// # Panics
1256-
///
1257-
/// Panics if `N` is 0. This check will most probably get changed to a compile time
1258-
/// error before this method gets stabilized.
1259-
///
12601319
/// # Examples
12611320
///
12621321
/// ```
@@ -1271,12 +1330,22 @@ impl<T> [T] {
12711330
/// assert_eq!(v, &[1, 1, 2, 2, 0]);
12721331
/// ```
12731332
///
1333+
/// It doesn't compile if chunk size is zero:
1334+
/// ```compile_fail
1335+
/// #![feature(array_chunks)]
1336+
/// const N: usize = 0;
1337+
/// let mut slice = [1, 2, 3, 4];
1338+
/// let _ = slice.array_chunks_mut::<N>();
1339+
/// ```
1340+
///
12741341
/// [`chunks_exact_mut`]: slice::chunks_exact_mut
12751342
#[unstable(feature = "array_chunks", issue = "74985")]
12761343
#[inline]
12771344
#[track_caller]
12781345
pub fn array_chunks_mut<const N: usize>(&mut self) -> ArrayChunksMut<'_, T, N> {
1279-
assert!(N != 0, "chunk size must be non-zero");
1346+
const {
1347+
assert!(N != 0, "chunk size must be non-zero");
1348+
}
12801349
ArrayChunksMut::new(self)
12811350
}
12821351

@@ -1287,11 +1356,6 @@ impl<T> [T] {
12871356
///
12881357
/// If `N` is greater than the size of the slice, it will return no windows.
12891358
///
1290-
/// # Panics
1291-
///
1292-
/// Panics if `N` is 0. This check will most probably get changed to a compile time
1293-
/// error before this method gets stabilized.
1294-
///
12951359
/// # Examples
12961360
///
12971361
/// ```
@@ -1304,12 +1368,22 @@ impl<T> [T] {
13041368
/// assert!(iter.next().is_none());
13051369
/// ```
13061370
///
1371+
/// It doesn't compile if window size is zero:
1372+
/// ```compile_fail
1373+
/// #![feature(array_windows)]
1374+
/// const N: usize = 0;
1375+
/// let slice = [0, 1, 2, 3];
1376+
/// let _ = slice.array_windows::<N>();
1377+
/// ```
1378+
///
13071379
/// [`windows`]: slice::windows
13081380
#[unstable(feature = "array_windows", issue = "75027")]
13091381
#[inline]
13101382
#[track_caller]
13111383
pub fn array_windows<const N: usize>(&self) -> ArrayWindows<'_, T, N> {
1312-
assert!(N != 0, "window size must be non-zero");
1384+
const {
1385+
assert!(N != 0, "window size must be non-zero");
1386+
}
13131387
ArrayWindows::new(self)
13141388
}
13151389

0 commit comments

Comments
 (0)