[pre-RFC] TryFromIterator and try_collect to enable collecting to arrays

Are you aware that FromIterator can collect into Result?

are you still working on this RFC?

Kinda, there was not a lot of positive feedback in this thread (for example not even a single person liked my post), so I am not sure if this is something that is wanted.

An RFC is a ton of work normally and involves a lot of discussion, but for this one I thought it would be very straight forward, as it seems to integrate very nicely into the existing eco-system.

but anyway there's another use case where TryFromIterator would be useful.

I also think that this trait would be very useful (for example, it could be used to collect with allocation failures not causing a panic), but I do not currently have the time to commit myself to a discussion about such a tiny feature spanning multiple months. (maybe I will find the time in the next days to finish it and submit an official RFC)

Your best bet would be to simply copy-paste the code of this RFC in your project (feel free to do so!), because this could take months (or sometimes years), until it reaches a stable release.

To be able to use the try_collect method on any iterator, you need to have an extension trait:

pub trait IteratorExt: Iterator {
    fn try_collect<B: TryFromIterator<Self::Item>>(self) -> Result<B, B::Error>
    where
        Self: Sized,
    {
        TryFromIterator::try_from_iter(self)
    }
}

impl<I: Iterator> IteratorExt for I {}

(note: since a recent rust release, the blanket impl is no longer working!)

Uh, that's not good. If it worked before and doesn't work anymore, that's a stable-to-stable regression we need to fix. Is there an issue link for this?

But also, many people are using this exact trait extension trait (e.g. itertools) and it's working fine for them. What are you doing differently that it stopped working for you?

Can you elaborate? It compiles just fine in the playground

I was referring to this: (sorry for the bad wording)

#![feature(never_type)]
use core::mem::{self, MaybeUninit};
use core::iter::{FromIterator, IntoIterator};

pub trait TryFromIterator<A>: Sized {
    type Error;

    fn try_from_iter<T: IntoIterator<Item = A>>(iter: T) -> Result<Self, Self::Error>;
}

impl<A, B> TryFromIterator<A> for B
where
    B: FromIterator<A>,
{
    type Error = !;

    fn try_from_iter<T: IntoIterator<Item = A>>(iter: T) -> Result<Self, Self::Error> {
        Ok(<Self as FromIterator<A>>::from_iter(iter))
    }
}
impl<A, const N: usize> TryFromIterator<A> for [A; N] {
    type Error = CollectArrayError;

    fn try_from_iter<T: IntoIterator<Item = A>>(iter: T) -> Result<Self, Self::Error> {
        let mut array: [MaybeUninit<A>; N] = MaybeUninit::uninit_array();
        let mut iterator = iter.into_iter();

        for (i, item) in array.iter_mut().enumerate() {
            if let Some(value) = iterator.next() {
                *item = MaybeUninit::new(value);
            } else {
                return Err(CollectArrayError::NotEnoughItems { missing: N - i });
            }
        }

        // One can not simply use mem::transmute, because of this issue:
        // https://github.com/rust-lang/rust/issues/61956
        let result: [A; N] = unsafe {
            // assert that we have exclusive ownership of the array
            let pointer: *mut [A; N] = &mut array as *mut _ as *mut [A; N];
            let result: [A; N] = pointer.read();
            // forget about the old array
            mem::forget(array);

            result
        };

        Ok(result)
    }
}

playground

It seems like I never tested those two impls together until yesterday (they work fine by themselves):

error[E0119]: conflicting implementations of trait `TryFromIterator<_>` for type `[_; _]`
  --> src/main.rs:21:1
   |
11 | / impl<A, B> TryFromIterator<A> for B
12 | | where
13 | |     B: FromIterator<A>,
14 | | {
...  |
19 | |     }
20 | | }
   | |_- first implementation here
21 |   impl<A, const N: usize> TryFromIterator<A> for [A; N] {
   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `[_; _]`
   |
   = note: downstream crates may implement trait `std::iter::FromIterator<_>` for type `[_; _]`

I think in std this would be no problem, because one can use specialization (but I am not even sure, if there is any use for the blanket impl, because it seems to be more restricting, than useful)

1 Like

This seems to not compile with any rustc that support const generics.