Listerone
Listerone

Reputation: 1601

How to convert &[u8] into &[u8; 64]?

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

Answers (1)

SCappella
SCappella

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

(playground)

Upvotes: 2

Related Questions