Fallible version of `array::map` (`array::try_map`)

Hello,

I just discovered array::map, which is very useful, because you can do stuff like this:

#![feature(array_map)]

use core::array;

#[must_use]
fn collect_array_opt<T, I, const N: usize>(iter: I) -> [Option<T>; N]
where
    I: IntoIterator<Item = T>,
{
    let mut iterator = iter.into_iter();

    [(); N].map(|_| iterator.next())
}

fn main() {
    let iterator = array::IntoIter::new(['a', 'b', 'ö', 'l', 'ü']).filter(char::is_ascii);

    let result = collect_array_opt::<_, _, 5>(iterator);

    assert_eq!(result, [Some('a'), Some('b'), Some('l'), None, None]);
}

(collecting into [Option<T>; N])

Now if we had a fallible version (try_map) one could do this:

#[derive(Debug, Clone, PartialEq)]
pub struct NotEnoughItems;

fn try_collect_array<A, T, const N: usize>(iter: T) -> Result<[A; N], NotEnoughItems>
where
    T: IntoIterator<Item = A>,
{
    let mut iterator = iter.into_iter();

    [(); N].try_map(|_| {
        if let Some(value) = iterator.next() {
            Ok(value)
        } else {
            Err(NotEnoughItems)
        }
    })
}

fn main() {
    let iterator = array::IntoIter::new(['a', 'b', 'ö', 'l', 'ü']).filter(char::is_ascii);

    let result = collect_array_opt::<_, _, 5>(iterator.clone());

    assert_eq!(result, [Some('a'), Some('b'), Some('l'), None, None]);
    assert_eq!(
        try_collect_array::<_, _, 3>(iterator.clone()),
        Ok(['a', 'b', 'l'])
    );
    assert_eq!(
        try_collect_array::<_, _, 4>(iterator.clone()),
        Err(NotEnoughItems)
    );
}

(which makes it possible to collect into an array without unsafe code!)

I think this would be a great addition to the standard library, but I currently do not have the time for an RFC, so I am leaving this here in the hope that someone else will make one

pub trait ArrayExt<T, const N: usize> {
    fn try_map<F, U, E>(self, f: F) -> Result<[U; N], E>
    where
        F: FnMut(T) -> Result<U, E>;
}

impl<T, const N: usize> ArrayExt<T, N> for [T; N] {
    fn try_map<F, U, E>(self, mut f: F) -> Result<[U; N], E>
    where
        F: FnMut(T) -> Result<U, E>,
    {
        let mut array: [MaybeUninit<U>; N] = MaybeUninit::uninit_array();
        let mut iterator = array::IntoIter::new(self);

        for item in array.iter_mut() {
            // NOTE: it is guranteed that this will not panic
            let next = iterator.next().unwrap();
            *item = MaybeUninit::new((f)(next)?);
        }

        // SAFETY: because of the previous loops all values are initialized
        Ok(array.map(|value: MaybeUninit<U>| unsafe { value.assume_init() }))
    }
}

playground

1 Like
5 Likes

Thank you, I did not know that this existed ._.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.