Skip to content

Commit a30f963

Browse files
committed
Add array::IntoIter::{empty, from_raw_parts}
`array::IntoIter` has a bunch of really handy logic for dealing with partial arrays, but it's currently hamstrung by only being creatable from a fully-initialized array. This PR adds two new constructors: - a safe & const `empty`, since `[].into_iter()` gives `<T, 0>`, not `<T, N>`. - an unsafe `from_raw_parts`, to allow experimentation with new uses. (Slice & vec iterators don't need `from_raw_parts` because you `from_raw_parts` the slice or vec instead, but there's no useful way to made a `<[T; N]>::from_raw_parts`, so I think this is a reasonable place to have one.)
1 parent 87dce6e commit a30f963

File tree

1 file changed

+129
-0
lines changed

1 file changed

+129
-0
lines changed

library/core/src/array/iter.rs

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,135 @@ impl<T, const N: usize> IntoIter<T, N> {
8484
IntoIterator::into_iter(array)
8585
}
8686

87+
/// Creates an iterator over the elements in a partially-initialized buffer.
88+
///
89+
/// If you have a fully-initialized array, then use [`IntoIterator`].
90+
/// But this is useful for returning partial results from unsafe code.
91+
///
92+
/// # Safety
93+
///
94+
/// - The `buffer[initialized]` elements must all be initialized.
95+
/// - The range must be canonical, with `initialized.start <= initialized.end`.
96+
/// - The range must in in-bounds for the buffer, with `initialized.end <= N`.
97+
/// (Like how indexing `[0][100..100]` fails despite the range being empty.)
98+
///
99+
/// It's sound to have more elements initialized than mentioned, though that
100+
/// will most likely result in them being leaked.
101+
///
102+
/// # Examples
103+
///
104+
/// ```
105+
/// #![feature(array_into_iter_constructors)]
106+
///
107+
/// #![feature(maybe_uninit_array_assume_init)]
108+
/// #![feature(maybe_uninit_uninit_array)]
109+
/// use std::array::IntoIter;
110+
/// use std::mem::MaybeUninit;
111+
///
112+
/// # // Hi! Thanks for reading the code. This is restricted to `Copy` because
113+
/// # // otherwise it could leak. A fully-general version this would need a drop
114+
/// # // guard to handle panics from the iterator, but this works for an example.
115+
/// fn next_chunk<T: Copy, const N: usize>(
116+
/// it: &mut impl Iterator<Item = T>,
117+
/// ) -> Result<[T; N], IntoIter<T, N>> {
118+
/// let mut buffer = MaybeUninit::uninit_array();
119+
/// let mut i = 0;
120+
/// while i < N {
121+
/// match it.next() {
122+
/// Some(x) => {
123+
/// buffer[i].write(x);
124+
/// i += 1;
125+
/// }
126+
/// None => {
127+
/// // SAFETY: We've initialized the first `i` items
128+
/// unsafe {
129+
/// return Err(IntoIter::from_raw_parts(buffer, 0..i));
130+
/// }
131+
/// }
132+
/// }
133+
/// }
134+
///
135+
/// // SAFETY: We've initialized all N items
136+
/// unsafe { Ok(MaybeUninit::array_assume_init(buffer)) }
137+
/// }
138+
///
139+
/// let r: [_; 4] = next_chunk(&mut (10..16)).unwrap();
140+
/// assert_eq!(r, [10, 11, 12, 13]);
141+
/// let r: IntoIter<_, 40> = next_chunk(&mut (10..16)).unwrap_err();
142+
/// assert_eq!(r.collect::<Vec<_>>(), vec![10, 11, 12, 13, 14, 15]);
143+
/// ```
144+
#[unstable(feature = "array_into_iter_constructors", issue = "88888888")]
145+
#[rustc_const_unstable(feature = "array_into_iter_constructors_const", issue = "88888888")]
146+
pub const unsafe fn from_raw_parts(
147+
buffer: [MaybeUninit<T>; N],
148+
initialized: Range<usize>,
149+
) -> Self {
150+
Self { data: buffer, alive: initialized }
151+
}
152+
153+
/// Creates an iterator over `T` which returns no elements.
154+
///
155+
/// If you just need an empty iterator, then use
156+
/// [`iter::empty()`](crate::iter::empty) instead.
157+
/// And if you need an empty array, use `[]`.
158+
///
159+
/// But this is useful when you need an `array::IntoIter<T, N>` *specifically*.
160+
///
161+
/// # Examples
162+
///
163+
/// ```
164+
/// #![feature(array_into_iter_constructors)]
165+
/// use std::array::IntoIter;
166+
///
167+
/// let empty = IntoIter::<i32, 3>::empty();
168+
/// assert_eq!(empty.len(), 0);
169+
/// assert_eq!(empty.as_slice(), &[]);
170+
///
171+
/// let empty = IntoIter::<std::convert::Infallible, 200>::empty();
172+
/// assert_eq!(empty.len(), 0);
173+
/// ```
174+
///
175+
/// `[1, 2].into_iter()` and `[].into_iter()` have different types
176+
/// ```should_fail
177+
/// #![feature(array_into_iter_constructors)]
178+
/// use std::array::IntoIter;
179+
///
180+
/// # // FIXME: use `.into_iter()` once the doc tests are in edition2021
181+
/// pub fn get_bytes(b: bool) -> IntoIter<i8, 4> {
182+
/// if b {
183+
/// IntoIter::new([1, 2, 3, 4])
184+
/// } else {
185+
/// IntoIter::new([]) // error[E0308]: mismatched types
186+
/// }
187+
/// }
188+
/// ```
189+
///
190+
/// But using this method you can get an empty iterator of appropriate size:
191+
/// ```
192+
/// #![feature(array_into_iter_constructors)]
193+
/// use std::array::IntoIter;
194+
///
195+
/// pub fn get_bytes(b: bool) -> IntoIter<i8, 4> {
196+
/// if b {
197+
/// IntoIter::new([1, 2, 3, 4])
198+
/// } else {
199+
/// IntoIter::empty()
200+
/// }
201+
/// }
202+
///
203+
/// assert_eq!(get_bytes(true).collect::<Vec<_>>(), vec![1, 2, 3, 4]);
204+
/// assert_eq!(get_bytes(false).collect::<Vec<_>>(), vec![]);
205+
/// ```
206+
#[unstable(feature = "array_into_iter_constructors", issue = "88888888")]
207+
pub fn empty() -> Self {
208+
let buffer = MaybeUninit::uninit_array();
209+
let initialized = 0..0;
210+
211+
// SAFETY: We're telling it that none of the elements are initialized,
212+
// which is trivially true. And ∀N: usize, 0 <= N.
213+
unsafe { Self::from_raw_parts(buffer, initialized) }
214+
}
215+
87216
/// Returns an immutable slice of all elements that have not been yielded
88217
/// yet.
89218
#[stable(feature = "array_value_iter", since = "1.51.0")]

0 commit comments

Comments
 (0)