core/array/drain.rs
1use crate::marker::{Destruct, PhantomData};
2use crate::mem::{ManuallyDrop, SizedTypeProperties, conjure_zst, transmute};
3use crate::ptr::{NonNull, drop_in_place, from_raw_parts_mut, without_provenance_mut};
4
5impl<'l, 'f, T, U, F: FnMut(T) -> U> Drain<'l, 'f, T, F> {
6 /// This function returns a function that lets you index the given array in const.
7 /// As implemented it can optimize better than iterators, and can be constified.
8 /// It acts like a sort of guard (owns the array) and iterator combined, which can be implemented
9 /// as it is a struct that implements const fn;
10 /// in that regard it is somewhat similar to an array::Iter implementing `UncheckedIterator`.
11 /// The only method you're really allowed to call is `next()`,
12 /// anything else is more or less UB, hence this function being unsafe.
13 /// Moved elements will not be dropped.
14 /// This will also not actually store the array.
15 ///
16 /// SAFETY: must only be called `N` times. Thou shalt not drop the array either.
17 #[rustc_const_unstable(feature = "array_try_map", issue = "79711")]
18 pub(super) const unsafe fn new<const N: usize>(
19 array: &'l mut ManuallyDrop<[T; N]>,
20 f: &'f mut F,
21 ) -> Self {
22 // dont drop the array, transfers "ownership" to Self
23 let ptr: NonNull<T> = NonNull::from_mut(array).cast();
24 // SAFETY:
25 // Adding `slice.len()` to the starting pointer gives a pointer
26 // at the end of `slice`. `end` will never be dereferenced, only checked
27 // for direct pointer equality with `ptr` to check if the drainer is done.
28 unsafe {
29 let end_or_len =
30 if T::IS_ZST { without_provenance_mut(N) } else { ptr.as_ptr().add(N) };
31 Self { ptr, end_or_len, f, l: PhantomData }
32 }
33 }
34}
35
36/// See [`Drain::new`]; this is our fake iterator.
37#[unstable(feature = "array_try_map", issue = "79711")]
38pub(super) struct Drain<'l, 'f, T, F> {
39 // FIXME(const-hack): This is a slice::IterMut<'l>, replace when possible.
40 /// The pointer to the next element to return, or the past-the-end location
41 /// if the drainer is empty.
42 ///
43 /// This address will be used for all ZST elements, never changed.
44 /// As we "own" this array, we dont need to store any lifetime.
45 ptr: NonNull<T>,
46 /// For non-ZSTs, the non-null pointer to the past-the-end element.
47 /// For ZSTs, this is the number of unprocessed items.
48 end_or_len: *mut T,
49
50 f: &'f mut F,
51 l: PhantomData<&'l mut [T]>,
52}
53
54#[rustc_const_unstable(feature = "array_try_map", issue = "79711")]
55#[unstable(feature = "array_try_map", issue = "79711")]
56impl<T, U, F> const FnOnce<(usize,)> for &mut Drain<'_, '_, T, F>
57where
58 F: [const] FnMut(T) -> U,
59{
60 type Output = U;
61
62 /// This implementation is useless.
63 extern "rust-call" fn call_once(mut self, args: (usize,)) -> Self::Output {
64 self.call_mut(args)
65 }
66}
67#[rustc_const_unstable(feature = "array_try_map", issue = "79711")]
68#[unstable(feature = "array_try_map", issue = "79711")]
69impl<T, U, F> const FnMut<(usize,)> for &mut Drain<'_, '_, T, F>
70where
71 F: [const] FnMut(T) -> U,
72{
73 // FIXME(const-hack): ideally this would be an unsafe fn `next()`, and to use it you would instead `|_| unsafe { drain.next() }`.
74 extern "rust-call" fn call_mut(
75 &mut self,
76 (_ /* ignore argument */,): (usize,),
77 ) -> Self::Output {
78 if T::IS_ZST {
79 #[expect(ptr_to_integer_transmute_in_consts)]
80 // SAFETY:
81 // This is equivalent to `self.end_or_len.addr`, but that's not
82 // available in `const`. `self.end_or_len` doesn't have provenance,
83 // so transmuting is fine.
84 let len = unsafe { transmute::<*mut T, usize>(self.end_or_len) };
85 // SAFETY:
86 // The caller guarantees that this is never called more than N times
87 // (see `Drain::new`), hence this cannot underflow.
88 self.end_or_len = without_provenance_mut(unsafe { len.unchecked_sub(1) });
89 // its UB to call this more than N times, so returning more ZSTs is valid.
90 // SAFETY: its a ZST? we conjur.
91 (self.f)(unsafe { conjure_zst::<T>() })
92 } else {
93 // increment before moving; if `f` panics, we drop the rest.
94 let p = self.ptr;
95 // SAFETY: caller guarantees never called more than N times (see `Drain::new`)
96 self.ptr = unsafe { self.ptr.add(1) };
97 // SAFETY: we are allowed to move this.
98 (self.f)(unsafe { p.read() })
99 }
100 }
101}
102#[rustc_const_unstable(feature = "array_try_map", issue = "79711")]
103#[unstable(feature = "array_try_map", issue = "79711")]
104impl<T: [const] Destruct, F> const Drop for Drain<'_, '_, T, F> {
105 fn drop(&mut self) {
106 let slice = if T::IS_ZST {
107 from_raw_parts_mut::<[T]>(
108 self.ptr.as_ptr(),
109 #[expect(ptr_to_integer_transmute_in_consts)]
110 // SAFETY:
111 // This is equivalent to `self.end_or_len.addr`, but that's not
112 // available in `const`. `self.end_or_len` doesn't have provenance,
113 // so transmuting is fine.
114 unsafe {
115 transmute::<*mut T, usize>(self.end_or_len)
116 },
117 )
118 } else {
119 // SAFETY: we cant read more than N elements
120 unsafe {
121 from_raw_parts_mut::<[T]>(
122 self.ptr.as_ptr(),
123 // SAFETY: `start <= end`
124 self.end_or_len.offset_from_unsigned(self.ptr.as_ptr()),
125 )
126 }
127 };
128
129 // SAFETY: By the type invariant, we're allowed to drop all these. (we own it, after all)
130 unsafe { drop_in_place(slice) }
131 }
132}