Reputation: 1601
I'm writing code that takes 64-byte blocks and I want to enforce this size at the type level:
fn f(buf: &[u8; 64]) { ... }
However, the data is coming in as a slice &[u8]
. So, I'd like to be able to take subspaces of this and convert to &[u8; 64]
. It turns out that it the following conversion works:
let buf = (0..32).collect::<Vec<u8>>();
let z: &[u8; 32] = &(&buf[..]).try_into().unwrap();
But the following doesn't:
let buf = (0..64).collect::<Vec<u8>>();
let z: &[u8; 64] = &(&buf[..]).try_into().unwrap();
The difference being from 0 to 32 inclusive, the conversion works, but not for sizes above 32. What can I do to enable this &[u8]
-> &[u8; 64]
conversion without manually doing data copy?
Upvotes: 5
Views: 3833
Reputation: 10434
If so desired, you could use the code that Rust currently uses for sizes 0-32.
#[stable(feature = "try_from", since = "1.34.0")]
impl<'a, T, const N: usize> TryFrom<&'a [T]> for &'a [T; N]
where
[T; N]: LengthAtMost32,
{
type Error = TryFromSliceError;
fn try_from(slice: &[T]) -> Result<&[T; N], TryFromSliceError> {
if slice.len() == N {
let ptr = slice.as_ptr() as *const [T; N];
unsafe { Ok(&*ptr) }
} else {
Err(TryFromSliceError(()))
}
}
}
When constant generics are stabilized, the bound [T; N]: LengthAtMost32
will presumably be removed. Until then, you can implement your own function. Either use nightly with constant generics enabled or simply have a function for your specific size.
struct TryFromSliceError(());
fn slice_to_array_64<T>(slice: &[T]) -> Result<&[T; 64], TryFromSliceError> {
if slice.len() == 64 {
let ptr = slice.as_ptr() as *const [T; 64];
unsafe {Ok(&*ptr)}
} else {
Err(TryFromSliceError(()))
}
}
Upvotes: 2