Panic-less and allocation-less [u8;N] to [u64; N/8] in big endian

I'm trying to make a fast u8 to u64 array conversion. That is, take the u8 slice elements 8 by 8 and transform them into u64 big endian. The problem is that I'm collecting them into a Vec just so I can get the result and return if something is wrong. I didn't want to use allocations. I pass the result as a parameter to avoid allocations also.

use std::array::TryFromSliceError;

pub enum ArrayConversionError {

impl From<TryFromSliceError> for ArrayConversionError {
	fn from(_e: TryFromSliceError) -> Self { 

pub fn u8_to_u64_array(
	array: &[u8],
	result: &mut [u64],
) -> Result<(), ArrayConversionError> {
	for (i, v) in array
		.collect::<Result<Vec<[u8; 8]>, TryFromSliceError>>()?
		.map(|byte_slices| u64::from_be_bytes(*byte_slices))
		result[i] = v;

fn main() {
    let mut result = [0u64;1];
    u8_to_u64_array(&[0, 0, 0, 0, 0, 0, 23, 255], &mut result).unwrap();
    println!("{:?}", result);

Is there something that can be done so no allocation is needed?

fn convert<const N: usize, const N8: usize> (
    bytes: [u8; N8],
) -> [u64; N]
    assert_eq!(N.checked_mul(8), Some(N8));
    if N == 0 { return []; }
    let mut iter = bytes.array_chunks::<8>();
    [(); N].map(|()| u64::from_be_bytes(*

If you can't use .array_chunks() because you're not on nightly, you can use .chunks(8).map(|it: &[u8]| -> &[u8; 8] { it.try_into().unwrap() }) to polyfill it.

The branches, including the panicking branches, are removed once an explicit choice of N is given, since they're unreachable (provided you give correct values of N and N8).

  • You can achieve not having to bother writing the array lengths with a macro:

    macro_rules! convert_native_endian {( $const_array:expr $(,)? ) => ({
        const __SRC: [::core::primitive::u8; $const_array.len()] = $const_array;
        const __DST: [::core::primitive::u64; __SRC.len() / 8] = unsafe {
            ::core::mem::transmute(__SRC) /* compile error if `__SRC.len() % 8 != 0` */
    }) pub(in crate) use convert_native_endian;

    (it can be amended to always use big endianness, although it would require more fiddling and it wouldn't be pretty)


Here's one way to do this on nightly, with only one bounds check (the necessary one to make sure there's enough space in the result slice):

pub fn u8_to_u64_array(
	array: &[u8],
	result: &mut [u64],
) {
    let n = result.len();
    let (result, array) = (&mut result[..n], &array.as_chunks().0[..n]);
    for i in 0..n {
        result[i] = u64::from_be_bytes(array[i]);

The core of it is as_chunks on slice, which does the slice-of-T to slice-of-arrays-of-T conversion.


Assuming buffers have correct lengths and compiler is able to track this fact, ByteOrder::read_u64_into does not require any allocations and will compile down to a binary without panics (select "ASM" instead of "Run" to see generated assembly).

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.