0

I have two u16's and I would like to have them stored as a single array of u8's. I have used the .to_be_bytes() function to create bytes from the u16's

So I thought I could do something like this:

let n : u16 = 10;
let h : u16 = 16;

let bb = [n.to_be_bytes(), h.to_be_bytes()];

But this creates a [[u8; 2]; 2]

Then I can call concat() on it, but this gives me a vector:

let bb= [n.to_be_bytes(), h.to_be_bytes()].concat();

So, it seems in my mind like it should be a pretty doable job, but I can't find anything on it, how do I do this?

Grazosi
  • 603
  • 1
  • 10
  • What is the problem with having a `Vec`? – Herohtar Apr 07 '22 at 21:24
  • because I need it as a slice, so it seems weird to convert from two arrays -> vec -> slice rather than just two arrays -> array -> slice – Grazosi Apr 07 '22 at 21:28
  • There's nothing weird about that; it's pretty standard. If you want to actually have an array you have to create it up front as a mutable variable and then modify it. – Herohtar Apr 07 '22 at 21:35
  • @Herohtar I think he was referring to the creation of a `Vec` not the mutable copying. It is only 4 bytes and allocation is expensive. – Locke Apr 07 '22 at 21:41
  • @Locke True, but not necessarily an issue. It depends on how it is being used. There isn't any information in the question that shows whether its a necessary optimization or not. – Herohtar Apr 07 '22 at 21:45
  • 1
    Does this answer your question? [How to concatenate arrays of known lengths?](https://stackoverflow.com/questions/67041830/how-to-concatenate-arrays-of-known-lengths) – Chayim Friedman Apr 07 '22 at 22:01

2 Answers2

1

Creating a Vec<u8> and then taking a slice is a perfectly fine way to do it if you need/want to create the resulting value dynamically (at runtime). You can't dynamically create an array, since the size has to be known at compile time, so you will have to create a buffer up front and then fill it.

You can use copy_from_slice() to copy the slices into your array, but that only works if the slice is the same size as the destination. You can handle that by using split_at_mut() to get each half as a mutable slice.

In this particular case, it's also possible to do it as a one-liner with some bitwise operations to get everything into the right place before calling to_be_bytes() (Thanks to @Stargateur for the tip!)

fn concat_u16_copy(a: u16, b: u16) -> [u8; 4] {
    let mut c = [0u8; 4];
    let (left, right) = c.split_at_mut(2);
    left.copy_from_slice(&a.to_be_bytes());
    right.copy_from_slice(&b.to_be_bytes());
    c
}

fn concat_u16_shift(a: u16, b: u16) -> [u8; 4] {
    (((a as u32) << 16) | b as u32).to_be_bytes()
}

fn main() {
    let n : u16 = 10;
    let h : u16 = 16;

    let b = concat_u16_copy(n, h);
    println!("{b:?}");

    let bb = concat_u16_shift(n, h);
    println!("{bb:?}");
}

Playground

Interestingly, the compiler seems smart enough to turn both of those options into essentially the same code:

example::concat_u16_copy:
        movzx   eax, si
        shl     edi, 16
        or      eax, edi
        bswap   eax
        ret

example::concat_u16_shift:
        shl     edi, 16
        movzx   eax, si
        or      eax, edi
        bswap   eax
        ret

Godbolt

Herohtar
  • 5,347
  • 4
  • 31
  • 41
0

The byteorder crate is the recommended way to read/write big-endian and little-endian values from a buffer.

use byteorder::{ByteOrder, BigEndian};

fn u16_to_u8_array(a: u16, b: u16) -> [u8; 4] {
    let mut arr: [u8; 4] = [0; 4];

    BigEndian::write_u16(&mut arr[0..2], a);
    BigEndian::write_u16(&mut arr[2..4], b);
    arr
}
Locke
  • 7,626
  • 2
  • 21
  • 41