From ce850bf8733cf65f9c9c3fb9208edc113fa97e3d Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Wed, 29 Oct 2025 12:50:47 +0000 Subject: [PATCH 1/8] array_chunks: slightly improve docs --- library/core/src/array/mod.rs | 7 ++++--- library/core/src/iter/adapters/array_chunks.rs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 0dc10758a8560..312c8da107475 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -907,7 +907,7 @@ where /// All write accesses to this structure are unsafe and must maintain a correct /// count of `initialized` elements. /// -/// To minimize indirection fields are still pub but callers should at least use +/// To minimize indirection, fields are still pub but callers should at least use /// `push_unchecked` to signal that something unsafe is going on. struct Guard<'a, T> { /// The array to be initialized. @@ -925,7 +925,7 @@ impl Guard<'_, T> { #[inline] pub(crate) unsafe fn push_unchecked(&mut self, item: T) { // SAFETY: If `initialized` was correct before and the caller does not - // invoke this method more than N times then writes will be in-bounds + // invoke this method more than N times, then writes will be in-bounds // and slots will not be initialized more than once. unsafe { self.array_mut.get_unchecked_mut(self.initialized).write(item); @@ -954,7 +954,7 @@ impl Drop for Guard<'_, T> { /// `next` at most `N` times, the iterator can still be used afterwards to /// retrieve the remaining items. /// -/// If `iter.next()` panicks, all items already yielded by the iterator are +/// If `iter.next()` panics, all items already yielded by the iterator are /// dropped. /// /// Used for [`Iterator::next_chunk`]. @@ -986,6 +986,7 @@ fn iter_next_chunk_erased( buffer: &mut [MaybeUninit], iter: &mut impl Iterator, ) -> Result<(), usize> { + // if `Iterator::next` panics, this guard will drop already initialized items let mut guard = Guard { array_mut: buffer, initialized: 0 }; while guard.initialized < guard.array_mut.len() { let Some(item) = iter.next() else { diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs index 8f1744fc5fbb7..ff83b2010490f 100644 --- a/library/core/src/iter/adapters/array_chunks.rs +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -89,7 +89,7 @@ where match self.iter.next_chunk() { Ok(chunk) => acc = f(acc, chunk)?, Err(remainder) => { - // Make sure to not override `self.remainder` with an empty array + // Make sure to not overwrite `self.remainder` with an empty array // when `next` is called after `ArrayChunks` exhaustion. self.remainder.get_or_insert(remainder); From 786cca82d24e6a29e66e1d945492db911cae0020 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Wed, 10 Sep 2025 13:33:45 +0000 Subject: [PATCH 2/8] slice iter: replace manual saturating_sub --- library/core/src/slice/iter.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 7053ae86e732f..10dd6ada30260 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -2368,10 +2368,7 @@ impl<'a, T> Iterator for RChunks<'a, T> { } else { // Can't underflow because of the check above let end = self.v.len() - end; - let start = match end.checked_sub(self.chunk_size) { - Some(sum) => sum, - None => 0, - }; + let start = end.saturating_sub(self.chunk_size); let nth = &self.v[start..end]; self.v = &self.v[0..start]; Some(nth) @@ -2391,10 +2388,7 @@ impl<'a, T> Iterator for RChunks<'a, T> { unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { let end = self.v.len() - idx * self.chunk_size; - let start = match end.checked_sub(self.chunk_size) { - None => 0, - Some(start) => start, - }; + let start = end.saturating_sub(self.chunk_size); // SAFETY: mostly identical to `Chunks::__iterator_get_unchecked`. unsafe { from_raw_parts(self.v.as_ptr().add(start), end - start) } } @@ -2571,10 +2565,7 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { let end = self.v.len() - idx * self.chunk_size; - let start = match end.checked_sub(self.chunk_size) { - None => 0, - Some(start) => start, - }; + let start = end.saturating_sub(self.chunk_size); // SAFETY: see comments for `RChunks::__iterator_get_unchecked` and // `ChunksMut::__iterator_get_unchecked`, `self.v`. unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), end - start) } From c62abb65c773654c9e8f65f5f115a3fce441781c Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Thu, 11 Sep 2025 11:05:03 +0000 Subject: [PATCH 3/8] slice iter: cleanup and make similar Chunks[Mut]::nth --- library/core/src/slice/iter.rs | 44 +++++++++++++++------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 10dd6ada30260..8ca78941a8a55 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -1537,13 +1537,13 @@ impl<'a, T> Iterator for Chunks<'a, T> { #[inline] fn nth(&mut self, n: usize) -> Option { - let (start, overflow) = n.overflowing_mul(self.chunk_size); - // min(len) makes a wrong start harmless, but enables optimizing this to brachless code - let chunk_start = &self.v[start.min(self.v.len())..]; - let (nth, remainder) = chunk_start.split_at(self.chunk_size.min(chunk_start.len())); - if !overflow && start < self.v.len() { - self.v = remainder; - Some(nth) + if let Some(start) = n.checked_mul(self.chunk_size) + && start < self.v.len() + { + let rest = &self.v[start..]; + let (chunk, rest) = rest.split_at(self.chunk_size.min(rest.len())); + self.v = rest; + Some(chunk) } else { self.v = &self.v[..0]; // cheaper than &[] None @@ -1613,10 +1613,7 @@ impl<'a, T> DoubleEndedIterator for Chunks<'a, T> { None } else { let start = (len - 1 - n) * self.chunk_size; - let end = match start.checked_add(self.chunk_size) { - Some(res) => cmp::min(self.v.len(), res), - None => self.v.len(), - }; + let end = (start + self.chunk_size).min(self.v.len()); let nth_back = &self.v[start..end]; self.v = &self.v[..start]; Some(nth_back) @@ -1719,22 +1716,19 @@ impl<'a, T> Iterator for ChunksMut<'a, T> { #[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 { + if let Some(start) = n.checked_mul(self.chunk_size) + && start < self.v.len() + { + // SAFETY: `start < self.v.len()` ensures this is in bounds + let (_, rest) = unsafe { self.v.split_at_mut(start) }; + // SAFETY: `.min(rest.len()` ensures this is in bounds + let (chunk, rest) = unsafe { rest.split_at_mut(self.chunk_size.min(rest.len())) }; + self.v = rest; + // SAFETY: Nothing else points to or will point to the contents of this slice. + Some(unsafe { &mut *chunk }) + } else { self.v = &mut []; None - } else { - let end = match start.checked_add(self.chunk_size) { - Some(sum) => cmp::min(self.v.len(), sum), - None => self.v.len(), - }; - // SAFETY: The self.v contract ensures that any split_at_mut is valid. - let (head, tail) = unsafe { self.v.split_at_mut(end) }; - // SAFETY: The self.v contract ensures that any split_at_mut is valid. - let (_, nth) = unsafe { head.split_at_mut(start) }; - self.v = tail; - // SAFETY: Nothing else points to or will point to the contents of this slice. - Some(unsafe { &mut *nth }) } } From a7ee7c8cbef695faf8449d09c542d12127a6aba9 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Thu, 11 Sep 2025 15:09:21 +0000 Subject: [PATCH 4/8] slice iter: more cleanup --- library/core/src/slice/iter.rs | 110 +++++++++++++------------------ library/coretests/tests/slice.rs | 7 ++ 2 files changed, 52 insertions(+), 65 deletions(-) diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 8ca78941a8a55..925910400eade 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -1415,26 +1415,21 @@ impl<'a, T> Iterator for Windows<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> DoubleEndedIterator for Windows<'a, T> { #[inline] - fn next_back(&mut self) -> Option<&'a [T]> { - if self.size.get() > self.v.len() { - None - } else { - let ret = Some(&self.v[self.v.len() - self.size.get()..]); - self.v = &self.v[..self.v.len() - 1]; - ret - } + fn next_back(&mut self) -> Option { + self.nth_back(0) } #[inline] fn nth_back(&mut self, n: usize) -> Option { - let (end, overflow) = self.v.len().overflowing_sub(n); - if end < self.size.get() || overflow { + if let Some(end) = self.v.len().checked_sub(n) + && let Some(start) = end.checked_sub(self.size.get()) + { + let res = &self.v[start..end]; + self.v = &self.v[..end - 1]; + Some(res) + } else { self.v = &self.v[..0]; // cheaper than &[] None - } else { - let ret = &self.v[end - self.size.get()..end]; - self.v = &self.v[..end - 1]; - Some(ret) } } } @@ -1523,9 +1518,7 @@ impl<'a, T> Iterator for Chunks<'a, T> { if self.v.is_empty() { (0, Some(0)) } else { - let n = self.v.len() / self.chunk_size; - let rem = self.v.len() % self.chunk_size; - let n = if rem > 0 { n + 1 } else { n }; + let n = self.v.len().div_ceil(self.chunk_size); (n, Some(n)) } } @@ -1613,7 +1606,7 @@ impl<'a, T> DoubleEndedIterator for Chunks<'a, T> { None } else { let start = (len - 1 - n) * self.chunk_size; - let end = (start + self.chunk_size).min(self.v.len()); + let end = start + (self.v.len() - start).min(self.chunk_size); let nth_back = &self.v[start..end]; self.v = &self.v[..start]; Some(nth_back) @@ -1702,9 +1695,7 @@ impl<'a, T> Iterator for ChunksMut<'a, T> { if self.v.is_empty() { (0, Some(0)) } else { - let n = self.v.len() / self.chunk_size; - let rem = self.v.len() % self.chunk_size; - let n = if rem > 0 { n + 1 } else { n }; + let n = self.v.len().div_ceil(self.chunk_size); (n, Some(n)) } } @@ -1903,13 +1894,10 @@ impl<'a, T> Iterator for ChunksExact<'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) - } + self.v.split_at_checked(self.chunk_size).and_then(|(chunk, rest)| { + self.v = rest; + Some(chunk) + }) } #[inline] @@ -1925,14 +1913,14 @@ impl<'a, T> Iterator for ChunksExact<'a, T> { #[inline] fn nth(&mut self, n: usize) -> Option { - let (start, overflow) = n.overflowing_mul(self.chunk_size); - if start >= self.v.len() || overflow { + if let Some(start) = n.checked_mul(self.chunk_size) + && start < self.v.len() + { + self.v = &self.v[start..]; + self.next() + } else { self.v = &self.v[..0]; // cheaper than &[] None - } else { - let (_, snd) = self.v.split_at(start); - self.v = snd; - self.next() } } @@ -2061,15 +2049,11 @@ impl<'a, T> Iterator for ChunksExactMut<'a, T> { #[inline] fn next(&mut self) -> Option<&'a mut [T]> { - if self.v.len() < self.chunk_size { - None - } else { - // SAFETY: self.chunk_size is inbounds because we compared above against self.v.len() - let (head, tail) = unsafe { self.v.split_at_mut(self.chunk_size) }; - self.v = tail; - // SAFETY: Nothing else points to or will point to the contents of this slice. - Some(unsafe { &mut *head }) - } + // SAFETY: we have `&mut self`, so are allowed to temporarily materialize a mut slice + unsafe { &mut *self.v }.split_at_mut_checked(self.chunk_size).and_then(|(chunk, rest)| { + self.v = rest; + Some(chunk) + }) } #[inline] @@ -2085,15 +2069,15 @@ impl<'a, T> Iterator for ChunksExactMut<'a, T> { #[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 { + if let Some(start) = n.checked_mul(self.chunk_size) + && start < self.v.len() + { + // SAFETY: `start < self.v.len()` + self.v = unsafe { self.v.split_at_mut(start).1 }; + self.next() + } else { self.v = &mut []; None - } else { - // SAFETY: The self.v contract ensures that any split_at_mut is valid. - let (_, snd) = unsafe { self.v.split_at_mut(start) }; - self.v = snd; - self.next() } } @@ -2341,9 +2325,7 @@ impl<'a, T> Iterator for RChunks<'a, T> { if self.v.is_empty() { (0, Some(0)) } else { - let n = self.v.len() / self.chunk_size; - let rem = self.v.len() % self.chunk_size; - let n = if rem > 0 { n + 1 } else { n }; + let n = self.v.len().div_ceil(self.chunk_size); (n, Some(n)) } } @@ -2355,17 +2337,17 @@ impl<'a, T> Iterator for RChunks<'a, T> { #[inline] fn nth(&mut self, n: usize) -> Option { - let (end, overflow) = n.overflowing_mul(self.chunk_size); - if end >= self.v.len() || overflow { + if let Some(end) = n.checked_mul(self.chunk_size) + && end < self.v.len() + { + let end = self.v.len() - end; + let rest = &self.v[..end]; + let (rest, chunk) = rest.split_at(end.saturating_sub(self.chunk_size)); + self.v = rest; + Some(chunk) + } else { self.v = &self.v[..0]; // cheaper than &[] None - } else { - // Can't underflow because of the check above - let end = self.v.len() - end; - let start = end.saturating_sub(self.chunk_size); - let nth = &self.v[start..end]; - self.v = &self.v[0..start]; - Some(nth) } } @@ -2508,9 +2490,7 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { if self.v.is_empty() { (0, Some(0)) } else { - let n = self.v.len() / self.chunk_size; - let rem = self.v.len() % self.chunk_size; - let n = if rem > 0 { n + 1 } else { n }; + let n = self.v.len().div_ceil(self.chunk_size); (n, Some(n)) } } diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs index 110c4e5f3b406..6f60f71e8a477 100644 --- a/library/coretests/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -432,6 +432,13 @@ fn test_chunks_exact_mut_zip_aliasing() { assert_eq!(first, (&mut [0, 1][..], &[6, 7][..])); } +#[test] +fn test_chunks_zst() { + const SIZE: usize = 16; + let mut it = [(); usize::MAX].chunks(SIZE); + assert_eq!(it.nth_back(0), Some(&[(); SIZE - 1][..])); +} + #[test] fn test_rchunks_mut_zip_aliasing() { let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; From b60788e90534fdb6581ba2d8691ee636baf1981f Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Tue, 4 Nov 2025 07:39:34 +0000 Subject: [PATCH 5/8] slice iter: rm all overflowing_* and switch conditions to mv dependent code close --- library/core/src/slice/iter.rs | 136 ++++++++++++++++----------------- 1 file changed, 64 insertions(+), 72 deletions(-) diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 925910400eade..8e972fefbe82f 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -1601,15 +1601,15 @@ impl<'a, T> DoubleEndedIterator for Chunks<'a, T> { #[inline] fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); - if n >= len { - self.v = &self.v[..0]; // cheaper than &[] - None - } else { + if n < len { let start = (len - 1 - n) * self.chunk_size; let end = start + (self.v.len() - start).min(self.chunk_size); let nth_back = &self.v[start..end]; self.v = &self.v[..start]; Some(nth_back) + } else { + self.v = &self.v[..0]; // cheaper than &[] + None } } } @@ -1770,10 +1770,7 @@ impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> { #[inline] fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); - if n >= len { - self.v = &mut []; - None - } else { + if n < len { let start = (len - 1 - n) * self.chunk_size; let end = match start.checked_add(self.chunk_size) { Some(res) => cmp::min(self.v.len(), res), @@ -1786,6 +1783,9 @@ impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> { self.v = head; // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *nth_back }) + } else { + self.v = &mut []; + None } } } @@ -1952,15 +1952,15 @@ impl<'a, T> DoubleEndedIterator for ChunksExact<'a, T> { #[inline] fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); - if n >= len { - self.v = &self.v[..0]; // cheaper than &[] - None - } else { + if n < len { let start = (len - 1 - n) * self.chunk_size; let end = start + self.chunk_size; let nth_back = &self.v[start..end]; self.v = &self.v[..start]; Some(nth_back) + } else { + self.v = &self.v[..0]; // cheaper than &[] + None } } } @@ -2111,10 +2111,7 @@ impl<'a, T> DoubleEndedIterator for ChunksExactMut<'a, T> { #[inline] fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); - if n >= len { - self.v = &mut []; - None - } else { + if n < len { let start = (len - 1 - n) * self.chunk_size; let end = start + self.chunk_size; // SAFETY: The self.v contract ensures that any split_at_mut is valid. @@ -2124,6 +2121,9 @@ impl<'a, T> DoubleEndedIterator for ChunksExactMut<'a, T> { self.v = head; // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *nth_back }) + } else { + self.v = &mut []; + None } } } @@ -2307,16 +2307,12 @@ impl<'a, T> Iterator for RChunks<'a, T> { if self.v.is_empty() { None } else { - let len = self.v.len(); - let chunksz = cmp::min(len, self.chunk_size); - // SAFETY: split_at_unchecked just requires the argument be less - // than the length. This could only happen if the expression `len - - // chunksz` overflows. This could only happen if `chunksz > len`, - // which is impossible as we initialize it as the `min` of `len` and - // `self.chunk_size`. - let (fst, snd) = unsafe { self.v.split_at_unchecked(len - chunksz) }; - self.v = fst; - Some(snd) + let idx = self.v.len().saturating_sub(self.chunk_size); + // SAFETY: self.chunk_size() > 0, so 0 <= idx < self.v.len(). + // Thus `idx` is in-bounds for `self.v` and can be used as a valid argument for `split_at_mut_unchecked`. + let (rest, chunk) = unsafe { self.v.split_at_unchecked(idx) }; + self.v = rest; + Some(chunk) } } @@ -2389,17 +2385,16 @@ impl<'a, T> DoubleEndedIterator for RChunks<'a, T> { #[inline] fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); - if n >= len { - self.v = &self.v[..0]; // cheaper than &[] - None - } else { - // can't underflow because `n < len` + if n < len { let offset_from_end = (len - 1 - n) * self.chunk_size; let end = self.v.len() - offset_from_end; let start = end.saturating_sub(self.chunk_size); let nth_back = &self.v[start..end]; self.v = &self.v[end..]; Some(nth_back) + } else { + self.v = &self.v[..0]; // cheaper than &[] + None } } } @@ -2471,17 +2466,13 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { if self.v.is_empty() { None } else { - let sz = cmp::min(self.v.len(), self.chunk_size); - let len = self.v.len(); - // SAFETY: split_at_mut_unchecked just requires the argument be less - // than the length. This could only happen if the expression - // `len - sz` overflows. This could only happen if `sz > - // len`, which is impossible as we initialize it as the `min` of - // `self.v.len()` (e.g. `len`) and `self.chunk_size`. - let (head, tail) = unsafe { self.v.split_at_mut_unchecked(len - sz) }; - self.v = head; + let idx = self.v.len().saturating_sub(self.chunk_size); + // SAFETY: self.chunk_size() > 0, so 0 <= idx < self.v.len(). + // Thus `idx` is in-bounds for `self.v` and can be used as a valid argument for `split_at_mut_unchecked`. + let (rest, chunk) = unsafe { self.v.split_at_mut_unchecked(idx) }; + self.v = rest; // SAFETY: Nothing else points to or will point to the contents of this slice. - Some(unsafe { &mut *tail }) + Some(unsafe { &mut *chunk }) } } @@ -2502,12 +2493,9 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { #[inline] fn nth(&mut self, n: usize) -> Option<&'a mut [T]> { - let (end, overflow) = n.overflowing_mul(self.chunk_size); - if end >= self.v.len() || overflow { - self.v = &mut []; - None - } else { - // Can't underflow because of the check above + if let Some(end) = n.checked_mul(self.chunk_size) + && end < self.v.len() + { let end = self.v.len() - end; let start = match end.checked_sub(self.chunk_size) { Some(sum) => sum, @@ -2522,6 +2510,9 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { self.v = head; // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *nth }) + } else { + self.v = &mut []; + None } } @@ -2566,10 +2557,7 @@ impl<'a, T> DoubleEndedIterator for RChunksMut<'a, T> { #[inline] fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); - if n >= len { - self.v = &mut []; - None - } else { + if n < len { // can't underflow because `n < len` let offset_from_end = (len - 1 - n) * self.chunk_size; let end = self.v.len() - offset_from_end; @@ -2581,6 +2569,9 @@ impl<'a, T> DoubleEndedIterator for RChunksMut<'a, T> { self.v = tail; // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *nth_back }) + } else { + self.v = &mut []; + None } } } @@ -2711,14 +2702,14 @@ impl<'a, T> Iterator for RChunksExact<'a, T> { #[inline] fn nth(&mut self, n: usize) -> Option { - let (end, overflow) = n.overflowing_mul(self.chunk_size); - if end >= self.v.len() || overflow { + if let Some(end) = n.checked_mul(self.chunk_size) + && end < self.v.len() + { + self.v = &self.v[..self.v.len() - end]; + self.next() + } else { self.v = &self.v[..0]; // cheaper than &[] None - } else { - let (fst, _) = self.v.split_at(self.v.len() - end); - self.v = fst; - self.next() } } @@ -2751,10 +2742,7 @@ impl<'a, T> DoubleEndedIterator for RChunksExact<'a, T> { #[inline] fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); - if n >= len { - self.v = &self.v[..0]; // cheaper than &[] - None - } else { + if n < len { // now that we know that `n` corresponds to a chunk, // none of these operations can underflow/overflow let offset = (len - n) * self.chunk_size; @@ -2763,6 +2751,9 @@ impl<'a, T> DoubleEndedIterator for RChunksExact<'a, T> { let nth_back = &self.v[start..end]; self.v = &self.v[end..]; Some(nth_back) + } else { + self.v = &self.v[..0]; // cheaper than &[] + None } } } @@ -2875,16 +2866,17 @@ impl<'a, T> Iterator for RChunksExactMut<'a, T> { #[inline] fn nth(&mut self, n: usize) -> Option<&'a mut [T]> { - let (end, overflow) = n.overflowing_mul(self.chunk_size); - if end >= self.v.len() || overflow { - self.v = &mut []; - None - } else { - let len = self.v.len(); + if let Some(end) = n.checked_mul(self.chunk_size) + && end < self.v.len() + { + let idx = self.v.len() - end; // SAFETY: The self.v contract ensures that any split_at_mut is valid. - let (fst, _) = unsafe { self.v.split_at_mut(len - end) }; + let (fst, _) = unsafe { self.v.split_at_mut(idx) }; self.v = fst; self.next() + } else { + self.v = &mut []; + None } } @@ -2919,10 +2911,7 @@ impl<'a, T> DoubleEndedIterator for RChunksExactMut<'a, T> { #[inline] fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); - if n >= len { - self.v = &mut []; - None - } else { + if n < len { // now that we know that `n` corresponds to a chunk, // none of these operations can underflow/overflow let offset = (len - n) * self.chunk_size; @@ -2935,6 +2924,9 @@ impl<'a, T> DoubleEndedIterator for RChunksExactMut<'a, T> { self.v = tail; // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *nth_back }) + } else { + self.v = &mut []; + None } } } From 62e0a843c0453233c0ebdbe756939ab7511c1a9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 1 Dec 2025 19:44:21 +0100 Subject: [PATCH 6/8] also introduce Peekable::next_if_map_mut next to next_if_map --- library/core/src/iter/adapters/peekable.rs | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/library/core/src/iter/adapters/peekable.rs b/library/core/src/iter/adapters/peekable.rs index a55de75d56c6e..ee40d2b74c647 100644 --- a/library/core/src/iter/adapters/peekable.rs +++ b/library/core/src/iter/adapters/peekable.rs @@ -331,6 +331,8 @@ impl Peekable { /// If the closure panics, the next value will always be consumed and dropped /// even if the panic is caught, because the closure never returned an `Err` value to put back. /// + /// See also: [`next_if_map_mut`](Self::next_if_map_mut). + /// /// # Examples /// /// Parse the leading decimal number from an iterator of characters. @@ -419,6 +421,44 @@ impl Peekable { self.peeked = Some(unpeek); None } + + /// Gives a mutable reference to the next value of the iterator and applies a function `f` to it, + /// returning the result and advancing the iterator if `f` returns `Some`. + /// + /// Otherwise, if `f` returns `None`, the next value is kept for the next iteration. + /// + /// If `f` panics, the item that is consumed from the iterator as if `Some` was returned from `f`. + /// The value will be dropped. + /// + /// This is similar to [`next_if_map`](Self::next_if_map), except ownership of the item is not given to `f`. + /// This can be preferable if `f` would copy the item anyway. + /// + /// # Examples + /// + /// Parse the leading decimal number from an iterator of characters. + /// ``` + /// #![feature(peekable_next_if_map)] + /// let mut iter = "125 GOTO 10".chars().peekable(); + /// let mut line_num = 0_u32; + /// while let Some(digit) = iter.next_if_map_mut(|c| c.to_digit(10)) { + /// line_num = line_num * 10 + digit; + /// } + /// assert_eq!(line_num, 125); + /// assert_eq!(iter.collect::(), " GOTO 10"); + /// ``` + #[unstable(feature = "peekable_next_if_map", issue = "143702")] + pub fn next_if_map_mut(&mut self, f: impl FnOnce(&mut I::Item) -> Option) -> Option { + let unpeek = if let Some(mut item) = self.next() { + match f(&mut item) { + Some(result) => return Some(result), + None => Some(item), + } + } else { + None + }; + self.peeked = Some(unpeek); + None + } } #[unstable(feature = "trusted_len", issue = "37572")] From 1331c35f0279fb11266003d9859c0bd30216e07b Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Tue, 2 Dec 2025 13:56:48 +0530 Subject: [PATCH 7/8] std: sys: fs: uefi: Make time in FileAttr optional At least on OVMF, some files copied over from linux file system seem to have invalid time (year = 1980 and everything else 0). Since Rust allows time to be optional and we can return error, that seems to be the way to go for now. Signed-off-by: Ayush Singh --- library/std/src/sys/fs/uefi.rs | 28 +++++++------ library/std/src/sys/pal/uefi/time.rs | 59 ++++++++++++++++------------ 2 files changed, 50 insertions(+), 37 deletions(-) diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index bd4ae56974f0c..44f8b6e80f903 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -18,9 +18,8 @@ pub struct File(!); pub struct FileAttr { attr: u64, size: u64, - accessed: SystemTime, - modified: SystemTime, - created: SystemTime, + file_time: FileTimes, + created: Option, } pub struct ReadDir(!); @@ -66,15 +65,20 @@ impl FileAttr { } pub fn modified(&self) -> io::Result { - Ok(self.modified) + self.file_time + .modified + .ok_or(io::const_error!(io::ErrorKind::InvalidData, "modification time is not valid")) } pub fn accessed(&self) -> io::Result { - Ok(self.accessed) + self.file_time + .accessed + .ok_or(io::const_error!(io::ErrorKind::InvalidData, "last access time is not valid")) } pub fn created(&self) -> io::Result { - Ok(self.created) + self.created + .ok_or(io::const_error!(io::ErrorKind::InvalidData, "creation time is not valid")) } fn from_uefi(info: helpers::UefiBox) -> Self { @@ -82,8 +86,10 @@ impl FileAttr { Self { attr: (*info.as_ptr()).attribute, size: (*info.as_ptr()).file_size, - modified: uefi_fs::uefi_to_systemtime((*info.as_ptr()).modification_time), - accessed: uefi_fs::uefi_to_systemtime((*info.as_ptr()).last_access_time), + file_time: FileTimes { + modified: uefi_fs::uefi_to_systemtime((*info.as_ptr()).modification_time), + accessed: uefi_fs::uefi_to_systemtime((*info.as_ptr()).last_access_time), + }, created: uefi_fs::uefi_to_systemtime((*info.as_ptr()).create_time), } } @@ -627,9 +633,9 @@ mod uefi_fs { /// EDK2 FAT driver uses EFI_UNSPECIFIED_TIMEZONE to represent localtime. So for proper /// conversion to SystemTime, we use the current time to get the timezone in such cases. - pub(crate) fn uefi_to_systemtime(mut time: r_efi::efi::Time) -> SystemTime { + pub(crate) fn uefi_to_systemtime(mut time: r_efi::efi::Time) -> Option { time.timezone = if time.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE { - time::system_time_internal::now().unwrap().timezone + time::system_time_internal::now().timezone } else { time.timezone }; @@ -639,7 +645,7 @@ mod uefi_fs { /// Convert to UEFI Time with the current timezone. #[expect(dead_code)] fn systemtime_to_uefi(time: SystemTime) -> r_efi::efi::Time { - let now = time::system_time_internal::now().unwrap(); + let now = time::system_time_internal::now(); time.to_uefi_loose(now.timezone, now.daylight) } } diff --git a/library/std/src/sys/pal/uefi/time.rs b/library/std/src/sys/pal/uefi/time.rs index f9f90a454976a..28dacbe3068a7 100644 --- a/library/std/src/sys/pal/uefi/time.rs +++ b/library/std/src/sys/pal/uefi/time.rs @@ -24,7 +24,8 @@ pub const UNIX_EPOCH: SystemTime = SystemTime::from_uefi(r_efi::efi::Time { daylight: 0, pad1: 0, pad2: 0, -}); +}) +.unwrap(); const MAX_UEFI_TIME: SystemTime = SystemTime::from_uefi(r_efi::efi::Time { year: 9999, @@ -38,7 +39,8 @@ const MAX_UEFI_TIME: SystemTime = SystemTime::from_uefi(r_efi::efi::Time { daylight: 0, pad1: 0, pad2: 0, -}); +}) +.unwrap(); impl Instant { pub fn now() -> Instant { @@ -68,8 +70,11 @@ impl Instant { } impl SystemTime { - pub(crate) const fn from_uefi(t: r_efi::efi::Time) -> Self { - Self(system_time_internal::from_uefi(&t)) + pub(crate) const fn from_uefi(t: r_efi::efi::Time) -> Option { + match system_time_internal::from_uefi(&t) { + Some(x) => Some(Self(x)), + None => None, + } } pub(crate) const fn to_uefi( @@ -96,9 +101,8 @@ impl SystemTime { } pub fn now() -> SystemTime { - system_time_internal::now() - .map(Self::from_uefi) - .unwrap_or_else(|| panic!("time not implemented on this platform")) + Self::from_uefi(system_time_internal::now()) + .expect("time incorrectly implemented on this platform") } pub fn sub_time(&self, other: &SystemTime) -> Result { @@ -129,17 +133,18 @@ pub(crate) mod system_time_internal { const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24; const SYSTEMTIME_TIMEZONE: i64 = -1440 * SECS_IN_MINUTE as i64; - pub(crate) fn now() -> Option