Description
Proposal
This feature aims to introduce a standard way of fallibly creating a [T; N]
from an
IntoIterator<Item = T>
. It functions by passing an IntoIterator<Item = T>
which will result in a
[T; N]
, if and only if the IntoIterator
holds >= N
items. Otherwise, it returns an
array::IntoIter<T>
with the original IntoIterator
's items. The proposed method signature follows:
pub fn try_from_iter<I, const N: usize>(iter: I) -> Result<[I::Item; N], IntoIter<I::Item, N>>
where
I: IntoIterator,
Problem statement
In cases in which we want to construct a fixed-size array from a collection or iterator we currently
do not have a standard and safe way of doing so. While it is possible to do this without additional
allocation, this requires some use of unsafe
and thus leaves the implementer with more room for error
and potential UB
.
There has been much work done on fallible Iterator
methods such as try_collect
and try_fold
, and
more work that is being undertaken to stabilize things like try_process
, these do not strictly apply to
this. The purpose of this API is creating a fixed-size array from an iterator where the failure case does
not come from the Iterator
's Item
s, but solely from the Iterator
not holding enough Item
s.
Therefore, it seems like giving array a dedicated associated method that narrows the scope of failure
to the relationship between the size of the Iterator
and the size of the array would be the pragmatic
thing to do.
Additionally, the relatively recent addition of iter:next_chunk
means this implements basically for free.
Motivation, use-cases
array::try_from_iter
provides a safe API for creating fixed-size arrays [T; N]
from IntoIterator
s.
For a somewhat contrived example, say we want to create a [String; 32]
from a HashSet<String>
.
let set: HashSet<String> = HashSet::new();
/* add some items to the set... */
let Ok(my_array) = std::array::try_from_iter::<_, 32>(set) else {
/* handle error... */
};
Oftentimes it can be more efficient to deal with fixed-size arrays, as they do away with the inherent
indirection that come with many of the other collection types. This means arrays can be great when
lookup speeds are of high importance. The problem is that Rust does not make it particularly easy to
dynamically allocate fixed-size arrays at runtime without the use of unsafe
code. This feature allows
developers to turn their collections into fixed-size arrays of specified size in a safe and ergonomic
manner.
Naturally it can be the case that a collection or IntoIterator
does not hold enough elements to fill the
entire length of the array. If that happens try_from_iter
returns an array::IntoIter
that that holds
the elements of the original IntoIterator
.
As this change is additive its adaptation should be quite straight forward. There may of course an
argument to be made for choosing a different name with better discoverability.
The addition of any methods that can replace existing unsafe
code in crates should be an improvement, at
least as it pertains to safety.
Solution sketches
The implementation of this feature should be quite straight forward as it is essentially a wrapper around
iter::next_chunk
:
#[inline]
pub fn try_from_iter<I, const N: usize>(iter: I) -> Result<[I::Item; N], IntoIter<I::Item, N>>
where
I: IntoIterator,
{
iter.into_iter().next_chunk()
}
It turns the passed type/collection into iter
, an Iterator<Item = T>
and then calls next_chunk
.
Links and related work
What happens now?
This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals in its weekly meeting. You should receive feedback within a week or two.