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() }))
}
}