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

Does TryFromIterator exist in a way ? #1839

Closed
Kerollmops opened this issue Jan 1, 2017 · 13 comments
Closed

Does TryFromIterator exist in a way ? #1839

Kerollmops opened this issue Jan 1, 2017 · 13 comments
Labels
T-libs-api Relevant to the library API team, which will review and decide on the RFC.

Comments

@Kerollmops
Copy link
Contributor

Kerollmops commented Jan 1, 2017

I'm trying to implement the FromIterator but with a combination with the unstable TryFrom one.

Something like this:

pub trait TryFromIterator<A> {
    type Err;
    fn try_from_iter<T>(iter: T) -> Result<Self, Self::Err> where T: IntoIterator<Item=A>;
}

Does someone know something about this (kind of) trait or it doesn't exists and am I not the only one needing this ? I ask this to know if I create an rfcs or not.

I just realise that FromStr can be a special case of TryFrom-ref but for the &str type.

@clarfonthey
Copy link
Contributor

This doesn't really exist, although I can't see where this would be useful in general. Result already implements FromIterator in a way that returns either the first error or a sequence of values.

@Kerollmops
Copy link
Contributor Author

Ho ! you are right ! closing this...

@Centril Centril added the T-libs-api Relevant to the library API team, which will review and decide on the RFC. label Feb 23, 2018
@dgibson
Copy link

dgibson commented Apr 13, 2021

Hm, I've been contemplating a quite different case where I think this would make sense in the context of const generics.

Specifically [T; N] could implement TryFromIterator<Item=T>, failing if the iterator doesn't return exactly N elements.

@clarfonthey
Copy link
Contributor

That's extremely fair; I tried to make something like that and it definitely doesn't seem like that'd be doable ever: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=661c9693f15113e09e3e6d5d75cb2ad4

Might be worth filing another issue though.

@programmerjake
Copy link
Member

hmm, @clarfonthey your code seems buggy, you're comparing len to 10 instead of N...

@clarfonthey
Copy link
Contributor

I initially didn't include the generics and then decided to ><

@dgibson
Copy link

dgibson commented Apr 14, 2021

Hm, the errors there are all to do with a collision implementing FromIterator for Result. That'd be need, but it does indeed conflict.

I was thinking of having this trait explicitly invoked, which avoids that problem. Something like https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=338cf992b6c63687c31cf1c77767696a

I guess a matching try_collect() method on Iterator would make sense with that.

@dgibson
Copy link

dgibson commented Apr 14, 2021

@SOF3
Copy link

SOF3 commented Apr 15, 2021

Another possible use case is probably some variant of BTreeMap that returns Err if two keys are equal.

@dgibson
Copy link

dgibson commented Apr 16, 2021

@SOF3, sorry, what you mean by that is not at all obvious to me.

@SOF3
Copy link

SOF3 commented Apr 16, 2021

For example,

let a = vec![(1, 2), (3, 4)];
let b = vec![(1, 2), (1, 4)];

let x: Result<BTreeMap<u32, u32>, MapError> = a.into_iter().try_collect();
let y: Result<BTreeMap<u32, u32>, MapError> = b.into_iter().try_collect();
assert!(x.unwrap(), btreemap![1 => 2, 3 => 4]);
assert!(y.unwrap_err(), MapError(1)); // key 1 is duplicated

@dgibson
Copy link

dgibson commented Apr 16, 2021

@SOF3, ah, thanks, now I understand.

@JustForFun88
Copy link

If anyone is interested, then here is the code for the array:

#![feature(try_trait_v2)]
#![feature(try_trait_v2_residual)]

use core::fmt;
use core::iter::{self, FusedIterator};
use core::mem::{self, MaybeUninit};
use core::ops::ControlFlow;
use core::ops::Range;
use core::ops::{FromResidual, Residual, Try};
use core::ptr;

fn main() {
    const N: usize = 50;
    const ARRAY_LEN: usize = 3;
    
    // ////////////////////////////////////////////////////////////////////
    // Collect from IntoIterator<Item = V>,
    // ////////////////////////////////////////////////////////////////////

    let iter = (0..ARRAY_LEN).map(|_| 0..N);
    let array: [Vec<_>; ARRAY_LEN] = match TryFromIterator::try_from_iter(iter) {
        Ok(array) => array,
        Err(into_iter) => panic!("Unexpected result: {into_iter:?}"),
    };
    for vector in array {
        assert_eq!(vector, (0..N).collect::<Vec<_>>());
    }

    let iter = (0..(ARRAY_LEN - 1)).map(|_| 0..N);
    let result: Result<[Vec<_>; ARRAY_LEN], _> = TryFromIterator::try_from_iter(iter);

    match result {
        Ok(array) => panic!("Unexpected result: {array:?}"),
        Err(into_iter) => {
            assert_eq!(into_iter.len(), ARRAY_LEN - 1);
            for vector in into_iter {
                assert_eq!(vector, (0..N).collect::<Vec<_>>());
            }
        }
    }

    // ////////////////////////////////////////////////////////////////////
    // Collect from IntoIterator<Item = Result<V, E>>
    // ////////////////////////////////////////////////////////////////////

    let iter = (0..ARRAY_LEN).map(|_| (0..N).map(Result::<_, usize>::Ok));
    let result: Result<[Vec<_>; ARRAY_LEN], _> = match TryFromIterator::try_from_iter(iter) {
        Ok(result) => result,
        Err(into_iter) => panic!("Unexpected result: {into_iter:?}"),
    };

    match result {
        Ok(array) => {
            for vec in array {
                assert_eq!(vec, (0..N).collect::<Vec<_>>());
            }
        }
        Err(err) => panic!("Unexpected result: {err:?}"),
    }

    let iter = (0..ARRAY_LEN).map(|i| {
        (0..N).map(move |x| {
            if i < (ARRAY_LEN - 1) {
                Ok(x)
            } else {
                if x < N / 2 {
                    Ok(x)
                } else {
                    Err(x)
                }
            }
        })
    });

    let result: Result<[Vec<_>; ARRAY_LEN], _> = match TryFromIterator::try_from_iter(iter) {
        Ok(result) => result,
        Err(into_iter) => panic!("Unexpected result: {into_iter:?}"),
    };
    assert_eq!(result, Err(N / 2));

    let iter = (0..(ARRAY_LEN - 1)).map(|_| (0..N).map(Result::<_, usize>::Ok));
    let result: Result<Result<[Vec<_>; ARRAY_LEN], _>, _> = TryFromIterator::try_from_iter(iter);

    match result {
        Ok(inner_res) => panic!("Unexpected result: {inner_res:?}"),
        Err(into_iter) => {
            assert_eq!(into_iter.len(), ARRAY_LEN - 1);
            for vector in into_iter {
                assert_eq!(vector, (0..N).collect::<Vec<_>>());
            }
        }
    }

    // ////////////////////////////////////////////////////////////////////
    // Collect from IntoIterator<Item = Option<V>>
    // ////////////////////////////////////////////////////////////////////

    let iter = (0..ARRAY_LEN).map(|_| (0..N).map(Some));
    let result: Option<[Vec<_>; ARRAY_LEN]> = match TryFromIterator::try_from_iter(iter) {
        Ok(result) => result,
        Err(into_iter) => panic!("Unexpected result: {into_iter:?}"),
    };

    match result {
        Some(array) => {
            for vec in array {
                assert_eq!(vec, (0..N).collect::<Vec<_>>());
            }
        }
        None => panic!("Unexpected result"),
    }

    let iter = (0..ARRAY_LEN).map(|i| {
        (0..N).map(move |x| {
            if i < (ARRAY_LEN - 1) {
                Some(x)
            } else {
                if x < N / 2 {
                    Some(x)
                } else {
                    None
                }
            }
        })
    });

    let result: Option<[Vec<_>; ARRAY_LEN]> = match TryFromIterator::try_from_iter(iter) {
        Ok(result) => result,
        Err(into_iter) => panic!("Unexpected result: {into_iter:?}"),
    };
    assert_eq!(result, None);

    let iter = (0..(ARRAY_LEN - 1)).map(|_| (0..N).map(Some));
    let result: Result<Option<[Vec<_>; ARRAY_LEN]>, _> = TryFromIterator::try_from_iter(iter);

    match result {
        Ok(inner_res) => panic!("Unexpected result: {inner_res:?}"),
        Err(into_iter) => {
            assert_eq!(into_iter.len(), ARRAY_LEN - 1);
            for vector in into_iter {
                assert_eq!(vector, (0..N).collect::<Vec<_>>());
            }
        }
    }
}

pub trait TryFromIterator<A>: Sized {
    /// The type returned in the event of a conversion error.
    type Error;

    /// Attempts to perform the conversion.
    fn try_from_iter<Iter>(iter: Iter) -> Result<Self, Self::Error>
    where
        Iter: IntoIterator<Item = A>;
}

// ///////////////////////////////////////////////////////////////////////////////////
// TryFromIterator<I> for [T; N] where I: IntoIterator<Item = V>
// ///////////////////////////////////////////////////////////////////////////////////

impl<I, T, V, const N: usize> TryFromIterator<I> for [T; N]
where
    I: IntoIterator<Item = V>,
    T: FromIterator<V>,
{
    type Error = IntoIter<T, N>;

    #[inline]
    fn try_from_iter<Iter>(iter: Iter) -> Result<Self, Self::Error>
    where
        Iter: IntoIterator<Item = I>,
    {
        let mut iter = iter
            .into_iter()
            .map(|inner_iter| inner_iter.into_iter().map(NeverShortCircuit));
        try_collect_into_array(&mut iter).map(|NeverShortCircuit(array)| array)
    }
}

// ///////////////////////////////////////////////////////////////////////////////////
// TryFromIterator<I> for Result<[T; N], E> where I: IntoIterator<Item = Result<V, E>>
// ///////////////////////////////////////////////////////////////////////////////////

impl<I, T, V, E, const N: usize> TryFromIterator<I> for Result<[T; N], E>
where
    I: IntoIterator<Item = Result<V, E>>,
    T: FromIterator<V>,
{
    type Error = IntoIter<T, N>;

    #[inline]
    fn try_from_iter<Iter>(iter: Iter) -> Result<Self, Self::Error>
    where
        Iter: IntoIterator<Item = I>,
    {
        try_collect_into_array(&mut iter.into_iter())
    }
}

// ///////////////////////////////////////////////////////////////////////////////////
// TryFromIterator<I> for Option<[T; N]> where I: IntoIterator<Item = Option<V>>
// ///////////////////////////////////////////////////////////////////////////////////

impl<I, T, V, const N: usize> TryFromIterator<I> for Option<[T; N]>
where
    I: IntoIterator<Item = Option<V>>,
    T: FromIterator<V>,
{
    type Error = IntoIter<T, N>;

    #[inline]
    fn try_from_iter<Iter>(iter: Iter) -> Result<Option<[T; N]>, Self::Error>
    where
        Iter: IntoIterator<Item = I>,
    {
        try_collect_into_array(&mut iter.into_iter())
    }
}

/// An adapter for implementing non-try methods via the `Try` implementation.
///
/// Conceptually the same as `Result<T, !>`, but requiring less work in trait
/// solving and inhabited-ness checking and such, by being an obvious newtype
/// and not having `From` bounds lying around.
#[repr(transparent)]
struct NeverShortCircuit<T>(T);

/// Companion `Residual` type for `NeverShortCircuit<T>`. Represents an error that
/// can never occur. Because this enum has no variants, a value of this type can never exist.
///
/// See https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types for more information.
enum NeverShortCircuitResidual {}

impl<T> Try for NeverShortCircuit<T> {
    type Output = T;
    type Residual = NeverShortCircuitResidual;

    #[inline]
    fn branch(self) -> ControlFlow<NeverShortCircuitResidual, T> {
        ControlFlow::Continue(self.0)
    }

    #[inline]
    fn from_output(x: T) -> Self {
        NeverShortCircuit(x)
    }
}

impl<T> FromResidual for NeverShortCircuit<T> {
    #[inline]
    fn from_residual(never: NeverShortCircuitResidual) -> Self {
        match never {}
    }
}

impl<T> Residual<T> for NeverShortCircuitResidual {
    type TryType = NeverShortCircuit<T>;
}

/// Panic guard for incremental initialization of arrays.
///
/// Disarm the guard with `mem::forget` once the array has been initialized.
///
/// # Safety
///
/// All write accesses to this structure are unsafe and must maintain a correct
/// count of `initialized` elements.
struct Guard<'a, T, const N: usize> {
    /// The array to be initialized.
    array_mut: &'a mut [MaybeUninit<T>; N],
    /// The number of items that have been initialized so far.
    initialized: usize,
}

impl<T, const N: usize> Guard<'_, T, N> {
    /// Adds an item to the array and updates the initialized item counter.
    ///
    /// # Safety
    ///
    /// No more than N elements must be initialized.
    #[inline(always)]
    unsafe fn push_unchecked(&mut self, item: T) {
        // SAFETY: If `initialized` was correct before and the caller does not
        // invoke this method more than N times then writes will be in-bounds
        // and slots will not be initialized more than once.
        unsafe {
            self.array_mut
                .get_unchecked_mut(self.initialized)
                .write(item);
            self.initialized += 1;
        }
    }
}

impl<T, const N: usize> Drop for Guard<'_, T, N> {
    fn drop(&mut self) {
        debug_assert!(self.initialized <= N);

        // SAFETY:
        // 1. The `slice` will contain only initialized objects;
        // 2. `MaybeUninit<T>` is `#[repr(transparent)]` so it is guaranteed
        //    to have the same size, alignment, and ABI as T
        //    (https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#layout-1)
        unsafe {
            let slice: *mut [MaybeUninit<T>] = self.array_mut.get_unchecked_mut(..self.initialized);
            ptr::drop_in_place(slice as *mut [T]);
        }
    }
}

/// Pulls `N` items from `iter` and returns them as an array. If the iterator
/// yields fewer than `N` items, `Err` is returned containing an iterator over
/// the already yielded items.
///
/// Since the iterator is passed as a mutable reference and this function calls
/// `next` at most `N` times, the iterator can still be used afterwards to
/// retrieve the remaining items.
///
/// If `iter.next()` panicks, all items already yielded by the iterator are
/// dropped.
#[inline]
fn try_collect_into_array<Input, Iter, V, R, T, Out, const N: usize>(
    iter: &mut Input,
) -> Result<Out, IntoIter<T, N>>
where
    Input: Iterator<Item = Iter>,
    Iter: IntoIterator,
    Iter::Item: Try<Output = V, Residual = R>,
    T: FromIterator<V>,
    R: Residual<T>,
    Out: Try<Output = [T; N], Residual = R>,
{
    if N == 0 {
        // SAFETY: An empty array is always inhabited and has no validity invariants.
        return Ok(Try::from_output(unsafe { mem::zeroed() }));
    }

    // SAFETY: The `assume_init` is safe because the type we are claiming to have
    // initialized here is a bunch of `MaybeUninit`s, which do not require initialization.
    let mut array: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
    let mut guard = Guard {
        array_mut: &mut array,
        initialized: 0,
    };

    for _ in 0..N {
        match iter.next() {
            Some(item_result) => {
                let result: <R as Residual<T>>::TryType =
                    try_process(item_result.into_iter(), |iter| iter.collect::<T>());

                let item: T = match result.branch() {
                    ControlFlow::Break(r) => {
                        return Ok(FromResidual::from_residual(r));
                    }
                    ControlFlow::Continue(elem) => elem,
                };

                // SAFETY: `guard.initialized` starts at 0, which means push can be called
                // at most N times, which this loop does.
                unsafe {
                    guard.push_unchecked(item);
                }
            }
            None => {
                let alive = 0..guard.initialized;
                mem::forget(guard);
                // SAFETY: `array` was initialized with exactly `initialized` number of elements.
                return Err(unsafe { IntoIter::new_unchecked(array, alive) });
            }
        }
    }

    mem::forget(guard);
    // SAFETY:
    // 1. All elements of the array were populated in the loop above;
    // 2. `MaybeUninit<T>` is `#[repr(transparent)]` so it is guaranteed
    //    to have the same size, alignment, and ABI as T
    //    (https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#layout-1)
    let output: [T; N] = unsafe { mem::transmute_copy(&array) };
    Ok(Try::from_output(output))
}

/// This is a helper type alias to make a bit more readable input-output type association.
///
/// For example if `T` is `Result<U, E>`, so
/// `ChangeOutputType<T, V> = <<Result<U, E> as Try>::Residual as Residual<V>>::TryType = Result<V, E>`
/// where `Result<U, E> as Try>::Residual = Result<Infallible, E>`.
///
/// If `T` is `Option<U>`, so
/// `ChangeOutputType<T, V> = <<Option<U> as Try>::Residual as Residual<V>>::TryType = Option<V>`
/// where `Option<U> as Try>::Residual = Option<Infallible>`.
///
/// If `T` is `NeverShortCircuit<U>`, so
/// `ChangeOutputType<T, V> = <<NeverShortCircuit<U> as Try>::Residual as Residual<V>>::TryType = NeverShortCircuit<V>`
/// where `NeverShortCircuit<U> as Try>::Residual = NeverShortCircuitResidual`.
type ChangeOutputType<T, V> = <<T as Try>::Residual as Residual<V>>::TryType;

/// Process the given iterator as if it yielded a the item's `Try::Output`
/// type instead. Any `Try::Residual`s encountered will stop the inner iterator
/// and be propagated back to the overall result.
#[inline]
fn try_process<I, T, R, F, U>(iter: I, mut f: F) -> ChangeOutputType<I::Item, U>
where
    I: Iterator,
    I::Item: Try<Output = T, Residual = R>,
    for<'a> F: FnMut(GenericShunt<'a, I, R>) -> U,
    R: Residual<U>,
{
    let mut residual = None;
    let shunt = GenericShunt {
        iter,
        residual: &mut residual,
    };
    let value = f(shunt);
    match residual {
        Some(r) => FromResidual::from_residual(r),
        None => Try::from_output(value),
    }
}

/// An iterator adapter that produces output as long as the underlying
/// iterator produces values where `Try::branch` says to `ControlFlow::Continue`.
///
/// If a `ControlFlow::Break` is encountered, the iterator stops and the
/// residual is stored.
struct GenericShunt<'a, I, R> {
    iter: I,
    residual: &'a mut Option<R>,
}

impl<I, R> Iterator for GenericShunt<'_, I, R>
where
    I: Iterator,
    I::Item: Try<Residual = R>,
{
    type Item = <I::Item as Try>::Output;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        if let Some(value) = self.iter.next() {
            match Try::branch(value) {
                ControlFlow::Continue(output) => return Some(output),
                ControlFlow::Break(residual) => {
                    *self.residual = Some(residual);
                    return None;
                }
            }
        }
        None
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        if self.residual.is_some() {
            (0, Some(0))
        } else {
            let (_, upper) = self.iter.size_hint();
            (0, upper)
        }
    }
}

/// A by-value [array] iterator.
///
/// # FIXME
///
/// This structure is similar to [`core::array::IntoIter`] and is duplicated here because
/// the [`IntoIter::new_unchecked`] method is not yet stable. Remove this structure in favor
/// of [`core::array::IntoIter`] after [`IntoIter::new_unchecked`] is stabilized.
///
/// [`core::array::IntoIter`]: https://doc.rust-lang.org/stable/core/array/struct.IntoIter.html#
/// [`IntoIter::new_unchecked`]: https://doc.rust-lang.org/stable/std/array/struct.IntoIter.html#method.new_unchecked
pub struct IntoIter<T, const N: usize> {
    /// This is the array we are iterating over.
    ///
    /// Elements with index `i` where `alive.start <= i < alive.end` have not
    /// been yielded yet and are valid array entries. Elements with indices `i
    /// < alive.start` or `i >= alive.end` have been yielded already and must
    /// not be accessed anymore! Those dead elements might even be in a
    /// completely uninitialized state!
    ///
    /// So the invariants are:
    /// - `data[alive]` is alive (i.e. contains valid elements)
    /// - `data[..alive.start]` and `data[alive.end..]` are dead (i.e. the
    ///   elements were already read and must not be touched anymore!)
    data: [MaybeUninit<T>; N],

    /// The elements in `data` that have not been yielded yet.
    ///
    /// Invariants:
    /// - `alive.end <= N`
    ///
    /// (And the `IndexRange` type requires `alive.start <= alive.end`.)
    alive: Range<usize>,
}

impl<T, const N: usize> IntoIter<T, N> {
    /// Creates a new iterator over the given `array`.
    ///
    /// # Examples
    ///
    /// ```
    /// use container::IntoIter;
    /// use std::ops::AddAssign;
    ///
    /// fn create_arr<T, const N: usize>(start: T, step: T) -> [T; N]
    /// where
    ///     T: AddAssign<T> + Copy,
    /// {
    ///     let mut outs: [T; N] = [start; N];
    ///     let mut element = step;
    ///     outs.iter_mut().skip(1).for_each(|v| {
    ///         *v += element;
    ///         element += step;
    ///     });
    ///     outs
    /// }
    ///
    /// let array: [i32; 100] = create_arr(0, 1);
    /// let iter = IntoIter::new(array);
    /// assert_eq!(iter.collect::<Vec<_>>(), (0..100).collect::<Vec<_>>());
    /// ```
    #[inline]
    pub fn new(array: [T; N]) -> 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(LukasKalbertodt): actually use `mem::transmute` here, once it
        // works with const generics:
        //     `mem::transmute::<[T; N], [MaybeUninit<T>; N]>(array)`
        //
        // Until then, we can use `mem::transmute_copy` to create a bitwise copy
        // as a different type, then forget `array` so that it is not dropped.
        unsafe {
            let iter = IntoIter {
                data: mem::transmute_copy(&array),
                alive: 0..N,
            };
            mem::forget(array);
            iter
        }
    }

    /// 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 be 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
    ///
    /// ```
    /// use container::IntoIter;
    /// use std::mem::{self, MaybeUninit};
    ///
    /// fn next_chunk<T: Copy, const N: usize>(
    ///     it: &mut impl Iterator<Item = T>,
    /// ) -> Result<[T; N], IntoIter<T, N>> {
    ///
    ///     // SAFETY: The `assume_init` is safe because the type we are claiming to have
    ///     // initialized here is a bunch of `MaybeUninit`s, which do not require initialization.
    ///     let mut buffer: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
    ///     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
    ///     Ok(unsafe { mem::transmute_copy(&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<_>>(), vec![10, 11, 12, 13, 14, 15]);
    /// ```
    #[inline]
    pub const unsafe fn new_unchecked(
        buffer: [MaybeUninit<T>; N],
        initialized: Range<usize>,
    ) -> Self {
        // SAFETY: one of our safety conditions is that the range is canonical.
        Self {
            data: buffer,
            alive: initialized,
        }
    }

    /// Returns an immutable slice of all elements that have not been yielded yet.
    ///
    /// # Examples
    ///
    /// ```
    /// use container::IntoIter;
    /// use std::ops::AddAssign;
    ///
    /// let array = [1, 2, 3, 4, 5];
    /// let iter = IntoIter::new(array);
    /// assert_eq!(iter.as_slice(), &[1, 2, 3, 4, 5]);
    /// ```
    #[inline]
    pub fn as_slice(&self) -> &[T] {
        unsafe {
            // SAFETY: We know that all elements within `alive` are properly initialized.
            let slice = self.data.get_unchecked(self.alive.clone());
            // SAFETY: casting `slice` to a `*const [T]` is safe since the `slice` is initialized,
            // and `MaybeUninit` is guaranteed to have the same layout as `T`.
            // The pointer obtained is valid since it refers to memory owned by `slice` which is a
            // reference and thus guaranteed to be valid for reads.
            &*(slice as *const [MaybeUninit<T>] as *const [T])
        }
    }

    /// Returns a mutable slice of all elements that have not been yielded yet.
    ///
    /// # Examples
    ///
    /// ```
    /// use container::IntoIter;
    /// use std::ops::AddAssign;
    ///
    /// let array = [1, 2, 3, 4, 5];
    /// let mut iter = IntoIter::new(array);
    /// assert_eq!(iter.as_mut_slice(), &mut [1, 2, 3, 4, 5]);
    /// ```
    #[inline]
    pub fn as_mut_slice(&mut self) -> &mut [T] {
        unsafe {
            // SAFETY: We know that all elements within `alive` are properly initialized.
            let slice = self.data.get_unchecked_mut(self.alive.clone());
            // SAFETY: casting `slice` to a `*mut [T]` is safe since the `slice` is initialized,
            // and `MaybeUninit` is guaranteed to have the same layout as `T`.
            // The pointer obtained is valid since it refers to memory owned by `slice` which is a
            // reference and thus guaranteed to be valid for reads and writes.
            &mut *(slice as *mut [MaybeUninit<T>] as *mut [T])
        }
    }
}

impl<T, const N: usize> Iterator for IntoIter<T, N> {
    type Item = T;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        // Get the next index from the front.
        //
        // Increasing `alive.start` by 1 maintains the invariant regarding
        // `alive`. However, due to this change, for a short time, the alive
        // zone is not `data[alive]` anymore, but `data[idx..alive.end]`.
        self.alive.next().map(|idx| {
            // Read the element from the array.
            // SAFETY: `idx` is an index into the former "alive" region of the
            // array. Reading this element means that `data[idx]` is regarded as
            // dead now (i.e. do not touch). As `idx` was the start of the
            // alive-zone, the alive zone is now `data[alive]` again, restoring
            // all invariants.
            unsafe { self.data.get_unchecked(idx).assume_init_read() }
        })
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        let len = self.len();
        (len, Some(len))
    }

    #[inline]
    fn fold<Acc, Fold>(mut self, init: Acc, mut fold: Fold) -> Acc
    where
        Fold: FnMut(Acc, Self::Item) -> Acc,
    {
        let data = &mut self.data;
        self.alive.by_ref().fold(init, |acc, idx| {
            // SAFETY: idx is obtained by folding over the `alive` range, which implies the
            // value is currently considered alive but as the range is being consumed each value
            // we read here will only be read once and then considered dead.
            fold(acc, unsafe { data.get_unchecked(idx).assume_init_read() })
        })
    }

    #[inline]
    fn count(self) -> usize {
        self.len()
    }

    #[inline]
    fn last(mut self) -> Option<Self::Item> {
        self.next_back()
    }
}

impl<T, const N: usize> DoubleEndedIterator for IntoIter<T, N> {
    #[inline]
    fn next_back(&mut self) -> Option<Self::Item> {
        // Get the next index from the back.
        //
        // Decreasing `alive.end` by 1 maintains the invariant regarding
        // `alive`. However, due to this change, for a short time, the alive
        // zone is not `data[alive]` anymore, but `data[alive.start..=idx]`.
        self.alive.next_back().map(|idx| {
            // Read the element from the array.
            // SAFETY: `idx` is an index into the former "alive" region of the
            // array. Reading this element means that `data[idx]` is regarded as
            // dead now (i.e. do not touch). As `idx` was the end of the
            // alive-zone, the alive zone is now `data[alive]` again, restoring
            // all invariants.
            unsafe { self.data.get_unchecked(idx).assume_init_read() }
        })
    }

    #[inline]
    fn rfold<Acc, Fold>(mut self, init: Acc, mut rfold: Fold) -> Acc
    where
        Fold: FnMut(Acc, Self::Item) -> Acc,
    {
        let data = &mut self.data;
        self.alive.by_ref().rfold(init, |acc, idx| {
            // SAFETY: idx is obtained by folding over the `alive` range, which implies the
            // value is currently considered alive but as the range is being consumed each value
            // we read here will only be read once and then considered dead.
            rfold(acc, unsafe { data.get_unchecked(idx).assume_init_read() })
        })
    }
}

impl<T, const N: usize> Drop for IntoIter<T, N> {
    fn drop(&mut self) {
        // SAFETY: This is safe: `as_mut_slice` returns exactly the sub-slice
        // of elements that have not been moved out yet and that remain
        // to be dropped.
        unsafe { ptr::drop_in_place(self.as_mut_slice()) }
    }
}

impl<T, const N: usize> ExactSizeIterator for IntoIter<T, N> {
    #[inline]
    fn len(&self) -> usize {
        self.alive.len()
    }
}

impl<T, const N: usize> FusedIterator for IntoIter<T, N> {}

impl<T: Clone, const N: usize> Clone for IntoIter<T, N> {
    #[inline]
    fn clone(&self) -> Self {
        // Note, we don't really need to match the exact same alive range, so
        // we can just clone into offset 0 regardless of where `self` is.
        //
        // SAFETY: The `assume_init` is safe because the type we are claiming to have
        // initialized here is a bunch of `MaybeUninit`s, which do not require initialization.
        let mut new = Self {
            data: unsafe { MaybeUninit::uninit().assume_init() },
            alive: 0..0,
        };

        // Clone all alive elements.
        for (src, dst) in iter::zip(self.as_slice(), &mut new.data) {
            // Write a clone into the new array, then update its alive range.
            // If cloning panics, we'll correctly drop the previous items.
            dst.write(src.clone());
            // This addition cannot overflow as we're iterating a slice
            new.alive = 0..(new.alive.end + 1);
        }

        new
    }
}

impl<T: fmt::Debug, const N: usize> fmt::Debug for IntoIter<T, N> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Only print the elements that were not yielded yet: we cannot
        // access the yielded elements anymore.
        f.debug_tuple("IntoIter").field(&self.as_slice()).finish()
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-libs-api Relevant to the library API team, which will review and decide on the RFC.
Projects
None yet
Development

No branches or pull requests

7 participants