Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FromIterator impl for [T; N] #69985

Closed
101 changes: 100 additions & 1 deletion library/core/src/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ use crate::cmp::Ordering;
use crate::convert::{Infallible, TryFrom};
use crate::fmt;
use crate::hash::{self, Hash};
use crate::iter::FromIterator;
use crate::marker::Unsize;
use crate::mem::MaybeUninit;
use crate::slice::{Iter, IterMut};

mod iter;
Expand Down Expand Up @@ -188,6 +190,104 @@ impl<T: fmt::Debug, const N: usize> fmt::Debug for [T; N] {
}
}

/// Return Error of the FromIterator impl for array
#[unstable(feature = "array_from_iter_impl", issue = "none")]
pub struct FillError<T, const N: usize> {
array: [MaybeUninit<T>; N],
len: usize,
}

#[unstable(feature = "array_from_iter_impl", issue = "none")]
impl<T, const N: usize> fmt::Display for FillError<T, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(
&format_args!("The iterator only returned {} items, but {} were needed", self.len(), N),
f,
)
}
}

#[unstable(feature = "array_from_iter_impl", issue = "none")]
impl<T: fmt::Debug, const N: usize> fmt::Debug for FillError<T, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FillError")
.field("array", &self.as_slice())
.field("len", &self.len())
.finish()
}
}

#[unstable(feature = "array_from_iter_impl", issue = "none")]
impl<T, const N: usize> Drop for FillError<T, N> {
fn drop(&mut self) {
// SAFETY: This is safe: `as_mut_slice` returns exactly the sub-slice
// of elements that have been initialized and need to be droped
unsafe { crate::ptr::drop_in_place(self.as_mut_slice()) }
}
}

#[unstable(feature = "array_from_iter_impl", issue = "none")]
impl<T, const N: usize> FillError<T, N> {
fn new() -> Self {
Self { array: MaybeUninit::uninit_array(), len: 0 }
}

/// Returns the how many elements were read from the given iterator.
pub fn len(&self) -> usize {
self.len
}

/// Returns an immutable slice of all initialized elements.
pub fn as_slice(&self) -> &[T] {
// SAFETY: We know that all elements from 0 to len are properly initialized.
unsafe { MaybeUninit::slice_assume_init_ref(&self.array[0..self.len]) }
}

/// Returns a mutable slice of all initialized elements.
pub fn as_mut_slice(&mut self) -> &mut [T] {
// SAFETY: We know that all elements from 0 to len are properly initialized.
unsafe { MaybeUninit::slice_assume_init_mut(&mut self.array[0..self.len]) }
}

/// Tries to initialize the left-over elements using `iter`.
pub fn fill<I: IntoIterator<Item = T>>(mut self, iter: I) -> Result<[T; N], FillError<T, N>> {
let mut iter = iter.into_iter();

for i in self.len..N {
if let Some(value) = iter.next() {
lperlaki marked this conversation as resolved.
Show resolved Hide resolved
self.array[i].write(value);
self.len += 1;
} else {
self.len = i;
return Err(self);
}
}

// SAFETY: The transmute here is actually safe. The docs of `MaybeUninit`
// promise:
//
// > `MaybeUninit<T>` is guaranteed to have the same size and alignment
// > as `T`.
//
// The docs even show a transmute from an array of `MaybeUninit<T>` to
// an array of `T`.
//
// With that, this initialization satisfies the invariants.
// FIXME: actually use `mem::transmute` here, once it
// works with const generics:
// `mem::transmute::<[MaybeUninit<T>; N], [T; N]>(array)`
Ok(unsafe { crate::ptr::read(&self.array as *const [MaybeUninit<T>; N] as *const [T; N]) })
}
}

#[unstable(feature = "array_from_iter_impl", issue = "none")]
impl<T, const N: usize> FromIterator<T> for Result<[T; N], FillError<T, N>> {
#[inline]
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
FillError::<T, N>::new().fill(iter)
}
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T, const N: usize> IntoIterator for &'a [T; N] {
type Item = &'a T;
Expand Down Expand Up @@ -403,7 +503,6 @@ impl<T, const N: usize> [T; N] {
where
F: FnMut(T) -> U,
{
use crate::mem::MaybeUninit;
struct Guard<T, const N: usize> {
dst: *mut T,
initialized: usize,
Expand Down
8 changes: 8 additions & 0 deletions library/core/tests/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,3 +374,11 @@ fn cell_allows_array_cycle() {
b3.a[0].set(Some(&b1));
b3.a[1].set(Some(&b2));
}

#[test]
fn array_collects() {
let v = vec![1, 2, 3, 4, 5];
let a: [i32; 5] = v.clone().into_iter().collect::<Result<[i32; 5], _>>().unwrap();

assert_eq!(v[..], a[..]);
}
1 change: 1 addition & 0 deletions library/core/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
#![feature(unsafe_block_in_unsafe_fn)]
#![feature(int_bits_const)]
#![deny(unsafe_op_in_unsafe_fn)]
#![feature(array_from_iter_impl)]

extern crate test;

Expand Down
1 change: 1 addition & 0 deletions library/std/src/primitive_docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ mod prim_pointer {}
/// Arrays of *any* size implement the following traits if the element type allows it:
///
/// - [`Debug`]
/// - [`iter::FromIterator`]
/// - [`IntoIterator`] (implemented for `&[T; N]` and `&mut [T; N]`)
/// - [`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`]
/// - [`Hash`]
Expand Down