Collect to slice

3dfa2a1
Opened by leonardo-m at 2020-04-21 18:13:26

This is an enhancement suggestion. Beside the current collect() to collection like Vec, Hash, etc, and the future collect to array:

https://github.com/rust-lang/rust/pull/69985

I find it useful to also collect to a slice when I don't know at compile-time the exact length of the resulting array, but I know an upper bound of its length.

I have adapted the following code from this crate: https://github.com/kchmck/collect_slice https://crates.io/crates/collect_slice

trait CollectSlice<'a>: Iterator {
    fn inner(&mut self, slice: &'a mut [Self::Item]) -> &'a [Self::Item];
    fn inner_mut(&mut self, slice: &'a mut [Self::Item]) -> &'a mut [Self::Item];

    fn collect_slice(&mut self, slice: &'a mut [Self::Item]) -> &'a [Self::Item] {
        let result = self.inner(slice);
        assert!(self.next().is_none());
        result
    }

    fn collect_slice_mut(&mut self, slice: &'a mut [Self::Item]) -> &'a mut [Self::Item] {
        let result = self.inner_mut(slice);
        assert!(self.next().is_none());
        result
    }
}

impl<I: ?Sized> CollectSlice<'a> for I where I: Iterator {
    fn inner(&mut self, slice: &'a mut [Self::Item]) -> &'a [Self::Item] {
        let count = slice.iter_mut().zip(self).fold(0, |count, (dest, item)| {
            *dest = item;
            count + 1
        });
        &slice[.. count]
    }

    fn inner_mut(&mut self, slice: &'a mut [Self::Item]) -> &'a mut [Self::Item] {
        let count = slice.iter_mut().zip(self).fold(0, |count, (dest, item)| {
            *dest = item;
            count + 1
        });
        &mut slice[.. count]
    }
}

This code should be improved and simplified. And as in Issue #69985 we could return a Result<> instead, and remove the asserts.

  1. I think I'm a little uncertain about passing in &mut [T] since that seems prone to "but I don't want to initialize my Ts" -- we'd probably want something like &mut [MaybeUninit<T>] though that raises questions as well.

    Mark Rousskov at 2020-04-21 16:16:55

  2. I think I'm a little uncertain about passing in &mut [T] since that seems prone to "but I don't want to initialize my Ts" -- we'd probably want something like &mut [MaybeUninit<T>] though that raises questions as well.

    Mark Rousskov at 2020-04-21 16:19:38

  3. I have found collect_slice useful, but I am still unsure if it's worth having in the std lib (while I am sure I want #69985, useful mostly for small arrays) because its usage is not very clean:

    let primes = &mut [0; 1000];
    let primes = (2_u32 .. 1000)
        .filter(|&b| is_prime(b))
        .collect_slice(primes);
    

    That is equivalent to:

    let primes = &mut [0; 1000];
    let mut index = 0;
    for b in 2_u32 .. 1000 {
        if is_prime(b) {
            primes[index] = b;
            index += 1;
        }
    }
    let primes = &[.. index];
    

    In all my usage cases I don't use collect_slice in the middle of an iterators chain because of memory ownership issues, so I use it only at the end of iterators chains like in the above example, and only when the number of items is high enough and I don't know it at compile-time.

    leonardo-m at 2020-04-21 16:39:48