Description
Proposal
Problem statement
Provide TryFrom<&[T]>
implementation for &[[T; N]]
as a more convenient wrapper around [T]::as_chunks
.
Motivation, use-cases
Unstable [T]::as_chunks
method allows converting &[T]
into &[[T; N]]
. However, to handle slices whose length doesn’t divide by N
the method returns (chunks, remainder)
tuple. If one wants to assert an empty remainder they may need to resort to destructuring the tuple and introducing if
statement. Similarly, it doesn’t mesh nicely with method chaining. A chain of methods needs to be stopped to verify the second element of the returned tuple before proceeding.
Introducing TryFrom<&[T]>
implementation for &[[T; N]]
offers a Result
-based interface for chunking a slice. Result
integrates with many interfaces of the language which results in more concise code.
Compare:
// Before:
fn pairs(slice: &[u8]) -> Result<&[[u8; 2]]> {
let (chunks, remainder) = slice.as_chunks::<2>();
if remainder.is_empty() {
Ok(chuns)
} else {
Err(SomeError)
}
}
// Alternatively with let-else:
fn pairs(slice: &[u8]) -> Result<&[[u8; 2]]> {
let (chunks, []) = slice.as_chunks::<2>() else {
return Err(SomeError);
};
Ok(chunks)
}
// After, more concise with a single expression:
fn pairs(slice: &[u8]) -> Result<&[[u8; 2]]> {
slice.try_into().map_err(|_| SomeError)
}
// Before, with use of let-else:
let bytes = get_bytes_in_a_vector();
let (chunks, []) = bytes.as_chunks() else {
return Err(SomeError.into());
};
let u64s = chunks
.into_iter()
.map(|chunk| u64::from_le_bytes(*bytes))
.collect::<Vec<_>>()
// After, single statement with no need for temporary variables:
let u64s = get_bytes_in_a_vector()
.as_slice()
.try_into::<&[[u8; 8]]>(bytes)
.map_err(|_| SomeError)?
.into_iter()
.map(|chunk| u64::from_le_bytes(*bytes))
.collect::<Vec<_>>();
Solution sketches
Solution is implemented in rust-lang/rust#105316 and has the form:
impl<'a, T, const N: usize> TryFrom<&'a [T]> for &'a [[T; N]] {
type Error = TryFromSliceError;
fn try_from(slice: &'a [T]) -> Result<Self, Self::Error> {
let (chunks, remainder) = slice.as_chunks::<N>();
if remainder.is_empty() {
Ok(chunks)
} else {
Err(TryFromSliceError(()))
}
}
}