Skip to content

Commit 31e9171

Browse files
committed
Replace get_unchecked usage within iter.rs
1 parent 975a6b6 commit 31e9171

1 file changed

Lines changed: 78 additions & 10 deletions

File tree

src/iter.rs

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,34 @@ use core::iter::FusedIterator;
55
use core::mem::ManuallyDrop;
66
use core::{cmp, fmt, mem, ptr};
77

8+
/// Build a raw slice pointer over the live sub-range `array[start..end]` of an iterator's
9+
/// backing `ManuallyDrop<GenericArray<T, N>>`, without ever forming a reference (not even a
10+
/// transient `&[T]`) that spans the moved-out slots outside `start..end`.
11+
///
12+
/// The base pointer is taken via `addr_of!`/`addr_of_mut!` on the array place (the same raw
13+
/// cast `GenericArray::as_slice` uses), so the only reference - if any - is the one the
14+
/// *caller* materializes from the returned raw pointer at the use site.
15+
///
16+
/// Forms:
17+
/// - `raw_subslice!(const $array, $start, $end)` -> `*const [T]`
18+
/// - `raw_subslice!(mut $array, $start, $end)` -> `*mut [T]`
19+
///
20+
/// # Safety
21+
///
22+
/// The caller must ensure `$start <= $end <= N` (the iterator's `index <= index_back <= N`
23+
/// invariant) so the range is in bounds, and - for the `mut` form - that no other live
24+
/// borrow of the array overlaps `$start..$end`.
25+
macro_rules! raw_subslice {
26+
(const $array:expr, $start:expr, $end:expr) => {{
27+
let base = ::core::ptr::addr_of!(*$array) as *const T;
28+
::core::ptr::slice_from_raw_parts(base.add($start), $end - $start)
29+
}};
30+
(mut $array:expr, $start:expr, $end:expr) => {{
31+
let base = ::core::ptr::addr_of_mut!(*$array) as *mut T;
32+
::core::ptr::slice_from_raw_parts_mut(base.add($start), $end - $start)
33+
}};
34+
}
35+
836
/// An iterator that moves out of a [`GenericArray`]
937
pub struct GenericArrayIter<T, N: ArrayLength> {
1038
// Invariants: index <= index_back <= N
@@ -16,18 +44,38 @@ pub struct GenericArrayIter<T, N: ArrayLength> {
1644
}
1745

1846
impl<T, N: ArrayLength> GenericArrayIter<T, N> {
47+
/// Raw `*const T` to element 0 of the backing array.
48+
///
49+
/// Taken via a raw cast of the array's address (the same cast `GenericArray::as_slice`
50+
/// uses), so that callers offsetting into the live range never form a reference - not
51+
/// even a transient `&[T]` - spanning the moved-out slots in `..index`/`index_back..`.
52+
#[inline(always)]
53+
fn base_ptr(&self) -> *const T {
54+
ptr::addr_of!(*self.array) as *const T
55+
}
56+
1957
/// Returns the remaining items of this iterator as a slice
2058
#[inline(always)]
2159
pub fn as_slice(&self) -> &[T] {
22-
// SAFETY: index and index_back are guaranteed to be within bounds
23-
unsafe { self.array.get_unchecked(self.index..self.index_back) }
60+
// SAFETY: By the type invariant, `index <= index_back <= N`, so `index` is in
61+
// bounds and `index_back - index` does not exceed the backing allocation. We form
62+
// the slice directly over the live `index..index_back` range rather than letting
63+
// `GenericArray`'s `Deref` materialize a `&[T]` spanning all `0..N` and then
64+
// re-slicing: this way no reference ever spans the moved-out slots in `..index`
65+
// or `index_back..` (same discipline as `core::array::IntoIter::as_slice`). The
66+
// live elements are initialized and valid for reads. (Codegen is identical to the
67+
// old `get_unchecked` form; see `examples/asm.rs`.)
68+
unsafe { &*raw_subslice!(const self.array, self.index, self.index_back) }
2469
}
2570

2671
/// Returns the remaining items of this iterator as a mutable slice
2772
#[inline(always)]
2873
pub fn as_mut_slice(&mut self) -> &mut [T] {
29-
// SAFETY: index and index_back are guaranteed to be within bounds
30-
unsafe { self.array.get_unchecked_mut(self.index..self.index_back) }
74+
// SAFETY: By the type invariant, `index <= index_back <= N`, so `index` is in
75+
// bounds and the length `index_back - index` does not exceed the backing
76+
// allocation. The slice spans only the live range, so it never references the
77+
// moved-out slots, and uniqueness holds because it is derived from `&mut self`.
78+
unsafe { &mut *raw_subslice!(mut self.array, self.index, self.index_back) }
3179
}
3280
}
3381

@@ -90,7 +138,10 @@ impl<T, N: ArrayLength> Iterator for GenericArrayIter<T, N> {
90138
#[inline]
91139
fn next(&mut self) -> Option<T> {
92140
if self.index < self.index_back {
93-
let p = unsafe { Some(ptr::read(self.array.get_unchecked(self.index))) };
141+
// SAFETY: `index < index_back <= N`, so element `index` is in bounds, alive,
142+
// and not yet read. Read it by value through the base pointer (no reference to
143+
// a moved-out slot is ever formed); `index` is advanced past it immediately.
144+
let p = unsafe { Some(ptr::read(self.base_ptr().add(self.index))) };
94145

95146
self.index += 1;
96147

@@ -112,7 +163,11 @@ impl<T, N: ArrayLength> Iterator for GenericArrayIter<T, N> {
112163
index_back,
113164
} = self;
114165

115-
let remaining = array.get_unchecked(*index..index_back);
166+
// SAFETY: `index <= index_back <= N`. Form a slice over only the live
167+
// `index..index_back` range (raw cast of the array address, so no reference
168+
// ever spans the moved-out slots), then read each element out by value exactly
169+
// once, advancing `index` so a panic in `f` drops only the tail.
170+
let remaining = &*raw_subslice!(const *array, *index, index_back);
116171

117172
remaining.iter().fold(init, |acc, src| {
118173
let value = ptr::read(src);
@@ -150,8 +205,11 @@ impl<T, N: ArrayLength> Iterator for GenericArrayIter<T, N> {
150205
// First consume values prior to the nth.
151206
let next_index = self.index + cmp::min(n, self.len());
152207

208+
// SAFETY: `index <= next_index <= index_back <= N`, so `index..next_index` is a
209+
// range of live, in-bounds elements. Drop exactly those in place via a raw slice
210+
// pointer (no reference spanning moved-out slots), then advance `index` past them.
153211
unsafe {
154-
ptr::drop_in_place(self.array.get_unchecked_mut(self.index..next_index));
212+
ptr::drop_in_place(raw_subslice!(mut self.array, self.index, next_index));
155213
}
156214

157215
self.index = next_index;
@@ -172,7 +230,10 @@ impl<T, N: ArrayLength> DoubleEndedIterator for GenericArrayIter<T, N> {
172230
if self.index < self.index_back {
173231
self.index_back -= 1;
174232

175-
unsafe { Some(ptr::read(self.array.get_unchecked(self.index_back))) }
233+
// SAFETY: after the decrement, `index <= index_back < N`, so element
234+
// `index_back` is in bounds, alive, and not yet read. Read it by value through
235+
// the base pointer; `index_back` already excludes it from the live range.
236+
unsafe { Some(ptr::read(self.base_ptr().add(self.index_back))) }
176237
} else {
177238
None
178239
}
@@ -190,7 +251,11 @@ impl<T, N: ArrayLength> DoubleEndedIterator for GenericArrayIter<T, N> {
190251
ref mut index_back,
191252
} = self;
192253

193-
let remaining = array.get_unchecked(index..*index_back);
254+
// SAFETY: `index <= index_back <= N`. Form a slice over only the live
255+
// `index..index_back` range (raw cast of the array address, so no reference
256+
// spans moved-out slots), then read each element out by value exactly once from
257+
// the back, decrementing `index_back` so a panic in `f` drops only the head.
258+
let remaining = &*raw_subslice!(const *array, index, *index_back);
194259

195260
remaining.iter().rfold(init, |acc, src| {
196261
let value = ptr::read(src);
@@ -210,8 +275,11 @@ impl<T, N: ArrayLength> DoubleEndedIterator for GenericArrayIter<T, N> {
210275
fn nth_back(&mut self, n: usize) -> Option<T> {
211276
let next_back = self.index_back - cmp::min(n, self.len());
212277

278+
// SAFETY: `index <= next_back <= index_back <= N`, so `next_back..index_back` is a
279+
// range of live, in-bounds elements. Drop exactly those in place via a raw slice
280+
// pointer (no reference spanning moved-out slots), then retreat `index_back`.
213281
unsafe {
214-
ptr::drop_in_place(self.array.get_unchecked_mut(next_back..self.index_back));
282+
ptr::drop_in_place(raw_subslice!(mut self.array, next_back, self.index_back));
215283
}
216284

217285
self.index_back = next_back;

0 commit comments

Comments
 (0)