diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index fe7b3576e2f..0dc277785e8 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -84,6 +84,135 @@ impl IntoIter { IntoIterator::into_iter(array) } + /// Creates an iterator over the elements in a partially-initialized buffer. + /// + /// If you have a fully-initialized array, then use [`IntoIterator`]. + /// But this is useful for returning partial results from unsafe code. + /// + /// # Safety + /// + /// - The `buffer[initialized]` elements must all be initialized. + /// - The range must be canonical, with `initialized.start <= initialized.end`. + /// - The range must in in-bounds for the buffer, with `initialized.end <= N`. + /// (Like how indexing `[0][100..100]` fails despite the range being empty.) + /// + /// It's sound to have more elements initialized than mentioned, though that + /// will most likely result in them being leaked. + /// + /// # Examples + /// + /// ``` + /// #![feature(array_into_iter_constructors)] + /// + /// #![feature(maybe_uninit_array_assume_init)] + /// #![feature(maybe_uninit_uninit_array)] + /// use std::array::IntoIter; + /// use std::mem::MaybeUninit; + /// + /// # // Hi! Thanks for reading the code. This is restricted to `Copy` because + /// # // otherwise it could leak. A fully-general version this would need a drop + /// # // guard to handle panics from the iterator, but this works for an example. + /// fn next_chunk( + /// it: &mut impl Iterator, + /// ) -> Result<[T; N], IntoIter> { + /// let mut buffer = MaybeUninit::uninit_array(); + /// let mut i = 0; + /// while i < N { + /// match it.next() { + /// Some(x) => { + /// buffer[i].write(x); + /// i += 1; + /// } + /// None => { + /// // SAFETY: We've initialized the first `i` items + /// unsafe { + /// return Err(IntoIter::new_unchecked(buffer, 0..i)); + /// } + /// } + /// } + /// } + /// + /// // SAFETY: We've initialized all N items + /// unsafe { Ok(MaybeUninit::array_assume_init(buffer)) } + /// } + /// + /// let r: [_; 4] = next_chunk(&mut (10..16)).unwrap(); + /// assert_eq!(r, [10, 11, 12, 13]); + /// let r: IntoIter<_, 40> = next_chunk(&mut (10..16)).unwrap_err(); + /// assert_eq!(r.collect::>(), vec![10, 11, 12, 13, 14, 15]); + /// ``` + #[unstable(feature = "array_into_iter_constructors", issue = "91583")] + #[rustc_const_unstable(feature = "const_array_into_iter_constructors", issue = "91583")] + pub const unsafe fn new_unchecked( + buffer: [MaybeUninit; N], + initialized: Range, + ) -> Self { + Self { data: buffer, alive: initialized } + } + + /// Creates an iterator over `T` which returns no elements. + /// + /// If you just need an empty iterator, then use + /// [`iter::empty()`](crate::iter::empty) instead. + /// And if you need an empty array, use `[]`. + /// + /// But this is useful when you need an `array::IntoIter` *specifically*. + /// + /// # Examples + /// + /// ``` + /// #![feature(array_into_iter_constructors)] + /// use std::array::IntoIter; + /// + /// let empty = IntoIter::::empty(); + /// assert_eq!(empty.len(), 0); + /// assert_eq!(empty.as_slice(), &[]); + /// + /// let empty = IntoIter::::empty(); + /// assert_eq!(empty.len(), 0); + /// ``` + /// + /// `[1, 2].into_iter()` and `[].into_iter()` have different types + /// ```should_fail,edition2021 + /// #![feature(array_into_iter_constructors)] + /// use std::array::IntoIter; + /// + /// pub fn get_bytes(b: bool) -> IntoIter { + /// if b { + /// [1, 2, 3, 4].into_iter() + /// } else { + /// [].into_iter() // error[E0308]: mismatched types + /// } + /// } + /// ``` + /// + /// But using this method you can get an empty iterator of appropriate size: + /// ```edition2021 + /// #![feature(array_into_iter_constructors)] + /// use std::array::IntoIter; + /// + /// pub fn get_bytes(b: bool) -> IntoIter { + /// if b { + /// [1, 2, 3, 4].into_iter() + /// } else { + /// IntoIter::empty() + /// } + /// } + /// + /// assert_eq!(get_bytes(true).collect::>(), vec![1, 2, 3, 4]); + /// assert_eq!(get_bytes(false).collect::>(), vec![]); + /// ``` + #[unstable(feature = "array_into_iter_constructors", issue = "91583")] + #[rustc_const_unstable(feature = "const_array_into_iter_constructors", issue = "91583")] + pub const fn empty() -> Self { + let buffer = MaybeUninit::uninit_array(); + let initialized = 0..0; + + // SAFETY: We're telling it that none of the elements are initialized, + // which is trivially true. And ∀N: usize, 0 <= N. + unsafe { Self::new_unchecked(buffer, initialized) } + } + /// Returns an immutable slice of all elements that have not been yielded /// yet. #[stable(feature = "array_value_iter", since = "1.51.0")] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index b0f9368b0c0..78383b54c5d 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -101,6 +101,7 @@ #![feature(const_align_of_val)] #![feature(const_alloc_layout)] #![feature(const_arguments_as_str)] +#![feature(const_array_into_iter_constructors)] #![feature(const_bigint_helper_methods)] #![feature(const_caller_location)] #![feature(const_cell_into_inner)] @@ -138,6 +139,7 @@ #![feature(const_type_name)] #![feature(const_default_impls)] #![feature(duration_consts_float)] +#![feature(maybe_uninit_uninit_array)] #![feature(ptr_metadata)] #![feature(slice_ptr_get)] #![feature(str_internals)]