1

So I have an array I've just created of u8 (a) and a slice of u16 (b). How can I get an array that is the result of concatenating the elements of a with the elements of b after turning these into u8?

I've tried a lot of modifications of the following code, but there is always some error.

fn main() {
    let a: [u8; 3] = [0x01, 0x02, 0x03];
    let b: &[u16] = &[0x0405, 0x0607, 0x0809];
    let result = [a,
                  b.iter().flat_map(|s| &s.to_be_bytes()).collect()]
                 .iter().flat_map(|e| e.iter()).collect();
    assert_eq!(result, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
}

I understand the error in that piece of code but I don't know how to fix it without creating another.

Like, I can create a as a Vec<u8> so that collect() can collect into something that implements std::iter::FromIterator. But it collects into Vec<&u8>.

I also have the feeling that it should be way more simple than my example. As if I'm missing a specific function.

Ibraheem Ahmed
  • 11,652
  • 2
  • 48
  • 54
BlueSialia
  • 101
  • 1
  • 13
  • Note that `&[u16]` is not a reference to an array, it is a [slice](https://doc.rust-lang.org/std/primitive.slice.html) – Ibraheem Ahmed Apr 01 '21 at 20:57
  • Ups! Yeah, my bad. Let's see if I can edit it. – BlueSialia Apr 01 '21 at 21:00
  • Do you really want `result` to be an *array*, or can it be a `Vec`. – kmdreko Apr 01 '21 at 21:13
  • https://docs.rs/byte-slice-cast/1.0.0/byte_slice_cast/index.html – Stargateur Apr 01 '21 at 21:13
  • @kmdreko I'm still learning Rust and right now I'm testing how comfortable it is to work with arrays only. Specially here because the lists are small. So if the difference between getting an `array` or a `vec` is small I prefer the former for this piece of code. – BlueSialia Apr 02 '21 at 12:35
  • @Stargateur That looks very helpful. I am trying to avoid dependencies, though. Firstly because I want to learn how to do things myself and secondly because I come from NPM dependency hell of web development. – BlueSialia Apr 02 '21 at 12:38

1 Answers1

4

The following works:

fn main() {
    let a: [u8; 3] = [0x01, 0x02, 0x03];
    let b: &[u16] = &[0x0405, 0x0607, 0x0809];
    let result: Vec<u8> = a.iter().copied()
        .chain(
            b.iter()
                .flat_map(|s| std::array::IntoIter::new(s.to_be_bytes()))
        )
        .collect();
    assert_eq!(result, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
}

This uses the newly added std::array::IntoIter, which converts an array [T; N] into an iterator of type T, as opposed to the .iter() which produces &T. This solves the issue of combining a u8 with a &u8.

Afterwards it uses .chain to attach both iterators together, which allows us to use different types of iterators that produce the same item, as opposed to using [a, b].iter(), which requires a and b to be the same type, as opposed to simply being iterators which produce the same type.

As a.iter().copied() and b.iter().flat_map(...) aren't the same type, you must use .chain instead of an array.

Filipe Rodrigues
  • 1,843
  • 2
  • 12
  • 21
  • use `to_be_bytes` don't make anysense even if OP use it this is not what should be used. – Stargateur Apr 01 '21 at 21:14
  • @Stargateur Depends on the use case, if OP wants to decode it with big endian, it's the appropriate, tool, if they want little endian, then `to_le_bytes`, if they don't care about endian then `to_ne_bytes`. I used what OP provided in their attempt. – Filipe Rodrigues Apr 01 '21 at 21:16
  • mmm I was think to use bit shift but yeah finally `to_be_bytes` is maybe more easy to use. – Stargateur Apr 01 '21 at 21:19
  • Thanks @FilipeRodrigues ! Wish I could upvote it. I have a question though. What's the difference between `std::array::IntoIter::new(s.to_be_bytes())` and `s.to_be_bytes().iter().copied()`? – BlueSialia Apr 02 '21 at 12:42
  • @BlueSialia Mostly none, aside from being different types, the former will be the same as `s.to_be_bytes().into_iter()` in the future (Currently blocked in https://github.com/rust-lang/rust/issues/25725), and `.into_iter()` and `.iter().copied()` aren't very different, see `https://stackoverflow.com/questions/34733811` for more details, but essentially for a container of type `T`, `into_iter()` consumes it and returns `T`s, `.iter()` borrows it and returns `&T`, and `.copied()` turns a `&T` into `T`, if `T` is trivially copy (`T: Copy`). – Filipe Rodrigues Apr 02 '21 at 13:46