From a30f96311aa2c0c1614132b1d5dd07bcfa9267fe Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 28 Nov 2021 15:34:53 -0800 Subject: [PATCH 1/4] 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 ``, not ``. - 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.) --- library/core/src/array/iter.rs | 129 +++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index fe7b3576e2f5f..bebf9aa28fae3 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::from_raw_parts(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 = "88888888")] + #[rustc_const_unstable(feature = "array_into_iter_constructors_const", issue = "88888888")] + pub const unsafe fn from_raw_parts( + 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 + /// #![feature(array_into_iter_constructors)] + /// use std::array::IntoIter; + /// + /// # // FIXME: use `.into_iter()` once the doc tests are in edition2021 + /// pub fn get_bytes(b: bool) -> IntoIter { + /// if b { + /// IntoIter::new([1, 2, 3, 4]) + /// } else { + /// IntoIter::new([]) // error[E0308]: mismatched types + /// } + /// } + /// ``` + /// + /// But using this method you can get an empty iterator of appropriate size: + /// ``` + /// #![feature(array_into_iter_constructors)] + /// use std::array::IntoIter; + /// + /// pub fn get_bytes(b: bool) -> IntoIter { + /// if b { + /// IntoIter::new([1, 2, 3, 4]) + /// } 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 = "88888888")] + pub 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::from_raw_parts(buffer, initialized) } + } + /// Returns an immutable slice of all elements that have not been yielded /// yet. #[stable(feature = "array_value_iter", since = "1.51.0")] From ef7c833c203e3de3785f8cbea3006da6237f730e Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 28 Nov 2021 19:53:32 -0800 Subject: [PATCH 2/4] Move the doc test to edition2021 --- library/core/src/array/iter.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index bebf9aa28fae3..9d4cb9284a5f4 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -173,28 +173,27 @@ impl IntoIter { /// ``` /// /// `[1, 2].into_iter()` and `[].into_iter()` have different types - /// ```should_fail + /// ```should_fail,edition2021 /// #![feature(array_into_iter_constructors)] /// use std::array::IntoIter; /// - /// # // FIXME: use `.into_iter()` once the doc tests are in edition2021 /// pub fn get_bytes(b: bool) -> IntoIter { /// if b { - /// IntoIter::new([1, 2, 3, 4]) + /// [1, 2, 3, 4].into_iter() /// } else { - /// IntoIter::new([]) // error[E0308]: mismatched types + /// [].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 { - /// IntoIter::new([1, 2, 3, 4]) + /// [1, 2, 3, 4].into_iter() /// } else { /// IntoIter::empty() /// } From 0b90204bc820ceafda004a5e4baf9f323b70d790 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Mon, 6 Dec 2021 01:12:59 -0800 Subject: [PATCH 3/4] Add tracking issue; make `empty` const too (unstably) --- library/core/src/array/iter.rs | 9 +++++---- library/core/src/lib.rs | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index 9d4cb9284a5f4..de3b768538527 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -141,8 +141,8 @@ impl IntoIter { /// 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 = "88888888")] - #[rustc_const_unstable(feature = "array_into_iter_constructors_const", issue = "88888888")] + #[unstable(feature = "array_into_iter_constructors", issue = "91583")] + #[rustc_const_unstable(feature = "const_array_into_iter_constructors", issue = "91583")] pub const unsafe fn from_raw_parts( buffer: [MaybeUninit; N], initialized: Range, @@ -202,8 +202,9 @@ impl IntoIter { /// 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 = "88888888")] - pub fn empty() -> Self { + #[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; diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index b0f9368b0c068..78383b54c5d1e 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)] From 9b86c5998c5b5b274b21651334a320aecc516dfc Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Mon, 6 Dec 2021 22:59:04 -0800 Subject: [PATCH 4/4] s/from_raw_parts/new_unchecked/ --- library/core/src/array/iter.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index de3b768538527..0dc277785e825 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -126,7 +126,7 @@ impl IntoIter { /// None => { /// // SAFETY: We've initialized the first `i` items /// unsafe { - /// return Err(IntoIter::from_raw_parts(buffer, 0..i)); + /// return Err(IntoIter::new_unchecked(buffer, 0..i)); /// } /// } /// } @@ -143,7 +143,7 @@ impl IntoIter { /// ``` #[unstable(feature = "array_into_iter_constructors", issue = "91583")] #[rustc_const_unstable(feature = "const_array_into_iter_constructors", issue = "91583")] - pub const unsafe fn from_raw_parts( + pub const unsafe fn new_unchecked( buffer: [MaybeUninit; N], initialized: Range, ) -> Self { @@ -210,7 +210,7 @@ impl IntoIter { // SAFETY: We're telling it that none of the elements are initialized, // which is trivially true. And ∀N: usize, 0 <= N. - unsafe { Self::from_raw_parts(buffer, initialized) } + unsafe { Self::new_unchecked(buffer, initialized) } } /// Returns an immutable slice of all elements that have not been yielded