Skip to content

Commit b92182a

Browse files
committed
Add slice::DrainRaw that moves out of a NonNull<[T]>
1 parent 2bddad7 commit b92182a

File tree

4 files changed

+283
-20
lines changed

4 files changed

+283
-20
lines changed

library/core/src/slice/drain.rs

+243
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
use crate::array;
2+
use crate::fmt;
3+
use crate::iter::{
4+
FusedIterator, TrustedFused, TrustedLen, TrustedRandomAccessNoCoerce, UncheckedIterator,
5+
};
6+
use crate::marker::PhantomData;
7+
use crate::mem::MaybeUninit;
8+
use crate::num::NonZero;
9+
use crate::ptr::NonNull;
10+
use crate::slice::NonNullIter;
11+
12+
/// An iterator which takes ownership of items out of a slice, dropping any
13+
/// remaining items when the iterator drops.
14+
///
15+
/// Note that, like a raw pointer, it's **up to you** to get the lifetime right.
16+
/// In some ways it's actually harder to get right, as the iterator interface
17+
/// appears safe, but as you promise when creating one of these, you still must
18+
/// ensure that the mentioned memory is usable the whole time this lives.
19+
///
20+
/// Ideally you won't be using this directly, but rather a version encapsulated
21+
/// in a safer interface, like `vec::IntoIter`.
22+
///
23+
/// This raw version may be removed in favour of a future language feature,
24+
/// such as using `unsafe<'a> Drain<'a, T>` instead of `DrainRaw<T>`.
25+
#[unstable(feature = "slice_drain_raw_iter", issue = "none")]
26+
pub struct DrainRaw<T>(NonNullIter<T>, PhantomData<T>);
27+
28+
#[unstable(feature = "slice_drain_raw_iter", issue = "none")]
29+
impl<T> Drop for DrainRaw<T> {
30+
fn drop(&mut self) {
31+
let slice = self.as_nonnull_slice();
32+
// SAFETY: By type invariant, we're allowed to drop the rest of the items.
33+
unsafe { slice.drop_in_place() };
34+
}
35+
}
36+
37+
#[unstable(feature = "slice_drain_raw_iter", issue = "none")]
38+
impl<T: fmt::Debug> fmt::Debug for DrainRaw<T> {
39+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40+
f.debug_tuple("DrainRaw").field(&self.0.make_shortlived_slice()).finish()
41+
}
42+
}
43+
44+
impl<T> DrainRaw<T> {
45+
/// Creates a new iterator which moves the `len` items starting at `ptr`
46+
/// while it's iterated, or drops them when the iterator is dropped.
47+
///
48+
/// # Safety
49+
///
50+
/// - `ptr` through `ptr.add(len)` must be a single allocated object
51+
/// such that that it's sound to `offset` through it.
52+
/// - All those elements must be readable, including being sufficiently aligned.
53+
/// - All those elements are valid for dropping.
54+
#[unstable(feature = "slice_drain_raw_iter", issue = "none")]
55+
#[inline]
56+
pub unsafe fn from_parts(ptr: NonNull<T>, len: usize) -> Self {
57+
// SAFETY: this function's safety conditions are stricter than NonNullIter,
58+
// and include allowing the type to drop the items in `Drop`.
59+
Self(unsafe { NonNullIter::from_parts(ptr, len) }, PhantomData)
60+
}
61+
62+
/// Returns a pointer to the remaining elements of the iterator
63+
#[unstable(feature = "slice_drain_raw_iter", issue = "none")]
64+
#[inline]
65+
pub fn as_nonnull_slice(&self) -> NonNull<[T]> {
66+
self.0.make_nonnull_slice()
67+
}
68+
69+
/// Equivalent to exhausting the iterator normally, but faster.
70+
#[unstable(feature = "slice_drain_raw_iter", issue = "none")]
71+
#[inline]
72+
pub fn drop_remaining(&mut self) {
73+
let all = self.forget_remaining();
74+
// SAFETY: We "forgot" these elements so our `Drop` won't drop them,
75+
// so it's ok to drop them here without risking double-frees.
76+
unsafe { all.drop_in_place() }
77+
}
78+
79+
/// Exhaust the iterator without actually dropping the rest of the items.
80+
///
81+
/// Returns the forgotten items.
82+
#[unstable(feature = "slice_drain_raw_iter", issue = "none")]
83+
#[inline]
84+
pub fn forget_remaining(&mut self) -> NonNull<[T]> {
85+
let all = self.as_nonnull_slice();
86+
self.0.exhaust();
87+
all
88+
}
89+
}
90+
91+
impl<T> UncheckedIterator for DrainRaw<T> {
92+
#[inline]
93+
unsafe fn next_unchecked(&mut self) -> T {
94+
// SAFETY: we're a 1:1 mapping of the inner iterator, so if the caller
95+
// proved we have another item, the inner iterator has another one too.
96+
// Also, the `next_unchecked` means the returned item is no longer part
97+
// of the inner iterator, and thus `read`ing it here -- and giving it
98+
// to the caller who will (probably) drop it -- is ok.
99+
unsafe { self.0.next_unchecked().read() }
100+
}
101+
}
102+
103+
#[unstable(feature = "slice_drain_raw_iter", issue = "none")]
104+
impl<T> Iterator for DrainRaw<T> {
105+
type Item = T;
106+
107+
#[inline]
108+
fn next(&mut self) -> Option<T> {
109+
match self.0.next() {
110+
// SAFETY: The `next` means the returned item is no longer part of
111+
// the inner iterator, and thus `read`ing it here -- and giving it
112+
// to the caller who will (probably) drop it -- is ok.
113+
Some(ptr) => Some(unsafe { ptr.read() }),
114+
None => None,
115+
}
116+
}
117+
118+
#[inline]
119+
fn size_hint(&self) -> (usize, Option<usize>) {
120+
self.0.size_hint()
121+
}
122+
123+
#[inline]
124+
fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
125+
let clamped = self.len().min(n);
126+
// SAFETY: By construction, `clamped` is always in-bounds.
127+
// The skipped elements are removed from the inner iterator so won't be
128+
// dropped in `Drop`, so dropping there here is fine.
129+
unsafe {
130+
let to_drop = self.0.skip_forward_unchecked(clamped);
131+
to_drop.drop_in_place();
132+
}
133+
NonZero::new(n - clamped).map_or(Ok(()), Err)
134+
}
135+
136+
#[inline]
137+
fn count(self) -> usize {
138+
self.len()
139+
}
140+
141+
#[inline]
142+
fn next_chunk<const N: usize>(&mut self) -> Result<[T; N], core::array::IntoIter<T, N>> {
143+
let len = self.len();
144+
let clamped = len.min(N);
145+
146+
// SAFETY: By construction, `clamped` is always in-bounds.
147+
let to_copy = unsafe { self.0.skip_forward_unchecked(clamped) };
148+
if len >= N {
149+
// SAFETY: If we have more elements than were requested, they can be
150+
// read directly because arrays need no extra alignment.
151+
Ok(unsafe { to_copy.cast::<[T; N]>().read() })
152+
} else {
153+
let mut raw_ary = MaybeUninit::uninit_array();
154+
// SAFETY: If we don't have enough elements left, then copy all the
155+
// ones we do have into the local array, which cannot overlap because
156+
// new locals are always distinct storage.
157+
Err(unsafe {
158+
MaybeUninit::<T>::slice_as_mut_ptr(&mut raw_ary)
159+
.copy_from_nonoverlapping(to_copy.as_mut_ptr(), len);
160+
array::IntoIter::new_unchecked(raw_ary, 0..len)
161+
})
162+
}
163+
}
164+
165+
unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> Self::Item
166+
where
167+
Self: TrustedRandomAccessNoCoerce,
168+
{
169+
// SAFETY: the caller must guarantee that `i` is in bounds of the slice,
170+
// so the `get_unchecked_mut(i)` is guaranteed to pointer to an element
171+
// and thus guaranteed to be valid to dereference.
172+
//
173+
// Also note the implementation of `Self: TrustedRandomAccess` requires
174+
// that `T: Copy` so reading elements from the buffer doesn't invalidate
175+
// them for `Drop`.
176+
unsafe { self.as_nonnull_slice().get_unchecked_mut(i).read() }
177+
}
178+
}
179+
180+
#[unstable(feature = "slice_drain_raw_iter", issue = "none")]
181+
impl<T> DoubleEndedIterator for DrainRaw<T> {
182+
#[inline]
183+
fn next_back(&mut self) -> Option<T> {
184+
match self.0.next_back() {
185+
// SAFETY: The `next_back` means the returned item is no longer part of
186+
// the inner iterator, and thus `read`ing it here -- and giving it
187+
// to the caller who will (probably) drop it -- is ok.
188+
Some(ptr) => Some(unsafe { ptr.read() }),
189+
None => None,
190+
}
191+
}
192+
193+
#[inline]
194+
fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
195+
let clamped = self.len().min(n);
196+
// SAFETY: By construction, `clamped` is always in-bounds.
197+
// The skipped elements are removed from the inner iterator so won't be
198+
// dropped in `Drop`, so dropping there here is fine.
199+
unsafe {
200+
let to_drop = self.0.skip_backward_unchecked(clamped);
201+
to_drop.drop_in_place();
202+
}
203+
NonZero::new(n - clamped).map_or(Ok(()), Err)
204+
}
205+
}
206+
207+
#[unstable(feature = "slice_drain_raw_iter", issue = "none")]
208+
impl<T> ExactSizeIterator for DrainRaw<T> {
209+
fn is_empty(&self) -> bool {
210+
self.0.is_empty()
211+
}
212+
213+
fn len(&self) -> usize {
214+
self.0.len()
215+
}
216+
}
217+
218+
#[unstable(feature = "slice_drain_raw_iter", issue = "none")]
219+
impl<T> FusedIterator for DrainRaw<T> {}
220+
221+
#[unstable(feature = "slice_drain_raw_iter", issue = "none")]
222+
#[doc(hidden)]
223+
unsafe impl<T> TrustedFused for DrainRaw<T> {}
224+
225+
#[unstable(feature = "slice_drain_raw_iter", issue = "none")]
226+
unsafe impl<T> TrustedLen for DrainRaw<T> {}
227+
228+
#[doc(hidden)]
229+
#[unstable(issue = "none", feature = "std_internals")]
230+
#[rustc_unsafe_specialization_marker]
231+
pub trait NonDrop {}
232+
233+
// T: Copy as approximation for !Drop since get_unchecked does not advance self.ptr
234+
// and thus we can't implement drop-handling
235+
#[unstable(issue = "none", feature = "std_internals")]
236+
impl<T: Copy> NonDrop for T {}
237+
238+
// SAFETY: If they're accessing things in random order we don't know when to drop
239+
// things, so only allow this for `Copy` things where that doesn't matter.
240+
#[unstable(feature = "slice_drain_raw_iter", issue = "none")]
241+
unsafe impl<T: NonDrop> TrustedRandomAccessNoCoerce for DrainRaw<T> {
242+
const MAY_HAVE_SIDE_EFFECT: bool = false;
243+
}

library/core/src/slice/iter.rs

+14-18
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,6 @@ impl<T> AsRef<[T]> for IterMut<'_, T> {
411411
iterator! {struct IterMut<'a, T> => *mut T, &'a mut T, {}}
412412

413413
/// Iterator over all the `NonNull<T>` pointers to the elements of a slice.
414-
#[unstable(feature = "slice_non_null_iter", issue = "none")]
415414
#[must_use = "iterators are lazy and do nothing unless consumed"]
416415
pub struct NonNullIter<T> {
417416
/// The pointer to the next element to return, or the past-the-end location
@@ -425,33 +424,21 @@ pub struct NonNullIter<T> {
425424
end_or_len: *const T,
426425
}
427426

428-
#[unstable(feature = "slice_non_null_iter", issue = "none")]
429427
impl<T: fmt::Debug> fmt::Debug for NonNullIter<T> {
430428
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
431429
f.debug_tuple("NonNullIter").field(&self.make_shortlived_slice()).finish()
432430
}
433431
}
434432

435433
impl<T> NonNullIter<T> {
436-
/// Turn an iterator giving `&T`s into one giving `NonNull<T>`s.
437-
#[unstable(feature = "slice_non_null_iter", issue = "none")]
438-
pub fn from_slice_iter(Iter { ptr, end_or_len, .. }: Iter<'_, T>) -> Self {
439-
Self { ptr, end_or_len }
440-
}
441-
442-
/// Turn an iterator giving `&mut T`s into one giving `NonNull<T>`s.
443-
#[unstable(feature = "slice_non_null_iter", issue = "none")]
444-
pub fn from_slice_iter_mut(IterMut { ptr, end_or_len, .. }: IterMut<'_, T>) -> Self {
445-
Self { ptr, end_or_len }
446-
}
447-
448434
/// Creates a new iterator over the `len` items starting at `ptr`
449435
///
450436
/// # Safety
451437
///
452-
/// `ptr` through `ptr.add(len)` must be a single allocated object
453-
/// such that that
454-
#[unstable(feature = "slice_non_null_iter", issue = "none")]
438+
/// - `ptr` through `ptr.add(len)` must be a single allocated object
439+
/// such that that it's sound to `offset` through it.
440+
/// - All those elements must be readable
441+
/// - The caller must ensure both as long as the iterator is in use.
455442
#[inline]
456443
pub unsafe fn from_parts(ptr: NonNull<T>, len: usize) -> Self {
457444
// SAFETY: There are several things here:
@@ -479,7 +466,16 @@ impl<T> NonNullIter<T> {
479466
}
480467

481468
#[inline]
482-
unsafe fn non_null_to_item(p: NonNull<T>) -> <Self as Iterator>::Item {
469+
pub fn exhaust(&mut self) {
470+
if T::IS_ZST {
471+
self.end_or_len = without_provenance_mut(0);
472+
} else {
473+
self.end_or_len = self.ptr.as_ptr();
474+
}
475+
}
476+
477+
#[inline]
478+
fn non_null_to_item(p: NonNull<T>) -> <Self as Iterator>::Item {
483479
p
484480
}
485481
}

library/core/src/slice/iter/macros.rs

+20
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,26 @@ macro_rules! iterator {
134134
},
135135
)
136136
}
137+
138+
// This is not used on every type that uses this macro, but is more
139+
// convenient to implement here so it can use `post_inc_start`.
140+
#[allow(dead_code)]
141+
#[inline]
142+
pub(crate) unsafe fn skip_forward_unchecked(&mut self, offset: usize) -> NonNull<[T]> {
143+
// SAFETY: The caller guarantees the provided offset is in-bounds.
144+
let old_begin = unsafe { self.post_inc_start(offset) };
145+
NonNull::slice_from_raw_parts(old_begin, offset)
146+
}
147+
148+
// This is not used on every type that uses this macro, but is more
149+
// convenient to implement here so it can use `pre_dec_end`.
150+
#[allow(dead_code)]
151+
#[inline]
152+
pub(crate) unsafe fn skip_backward_unchecked(&mut self, offset: usize) -> NonNull<[T]> {
153+
// SAFETY: The caller guarantees the provided offset is in-bounds.
154+
let new_end = unsafe { self.pre_dec_end(offset) };
155+
NonNull::slice_from_raw_parts(new_end, offset)
156+
}
137157
}
138158

139159
#[allow(unused_lifetimes)]

library/core/src/slice/mod.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub mod sort;
3535

3636
mod ascii;
3737
mod cmp;
38+
mod drain;
3839
pub(crate) mod index;
3940
mod iter;
4041
mod raw;
@@ -46,8 +47,6 @@ mod specialize;
4647
#[doc(hidden)]
4748
pub use ascii::is_ascii_simple;
4849

49-
#[unstable(feature = "slice_non_null_iter", issue = "none")]
50-
pub use iter::NonNullIter;
5150
#[stable(feature = "rust1", since = "1.0.0")]
5251
pub use iter::{Chunks, ChunksMut, Windows};
5352
#[stable(feature = "rust1", since = "1.0.0")]
@@ -73,6 +72,11 @@ pub use iter::ArrayWindows;
7372
#[stable(feature = "slice_group_by", since = "1.77.0")]
7473
pub use iter::{ChunkBy, ChunkByMut};
7574

75+
use iter::NonNullIter;
76+
77+
#[unstable(feature = "slice_drain_raw_iter", issue = "none")]
78+
pub use drain::DrainRaw;
79+
7680
#[stable(feature = "split_inclusive", since = "1.51.0")]
7781
pub use iter::{SplitInclusive, SplitInclusiveMut};
7882

0 commit comments

Comments
 (0)