0

I have the following two variables: s, which has type Option<&[u8; 32]> and k, which has type &[u8; 32]. I would like to concatenate s and k into a byte array [u8; 64] using the chain method (I'm using chain instead of concat because I've read it is much better performance wise), however, it seems I'm doing something wrong.

Here is a simplified example of what I'm trying to do:

fn combine(s: Option<&[u8; 32]>, k : &[u8; 32]) -> [u8; 64]{
    let tmps = *s.unwrap();
    let tmpk = *k;
    let result = tmps.iter().chain(tmpk.iter()).collect();
    result
}   

fn main() {
    let s = [47u8; 32];
    let k = [23u8; 32];
    println!("s: {:?}", s);
    println!("k: {:?}", k);
    
    let sk = combine(Some(s), k);
    println!("sk: {:?}", sk);
}

The error I'm getting: a value of type '[u8; 64]' cannot be built from an iterator over elements of type '&u8' Here is the link to Rust Playground with the code above.

Ziva
  • 3,181
  • 15
  • 48
  • 80
  • Does this answer your question? [How do I collect into an array?](https://stackoverflow.com/questions/26757355/how-do-i-collect-into-an-array) – kmdreko May 23 '21 at 16:13
  • You cannot `.collect()` into an array. The most straightforward methods are to either collect into a `Vec` and then use `.try_into()`, or use the `ArrayVec` crate. – kmdreko May 23 '21 at 16:15
  • @kmdreko But it seems that the function 'collect' doesn't work at all. I never assigned sk a type, but after your message when I try to do `let salt_with_key: Vec = tmp.iter().chain(key.iter()).collect();` I get the same issue. – Ziva May 23 '21 at 16:32
  • 1
    You're also trying to go from `&u8`s to `u8`s, you can add `.copied()` before collecting. See it working [here](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=05321216925f3ce24fe6dadf1b692a08) on the playground. – kmdreko May 23 '21 at 16:37
  • If you are particularly concerned about performance, using `ArrayVec` will likely be better since it would avoid an allocation and extra copy (potentially). – kmdreko May 23 '21 at 16:41
  • @kmdreko Thanks for the playground link! Regarding the ArrayVec - can you point me to some examples how to use it? – Ziva May 23 '21 at 16:44
  • 1
    `chain` and `collect` methods work with iterators. Copying the whole slice is considered faster than one by one element. But what you @Zive have read about `collect` is that it uses specialization to implement bulk copy on slice iterators directly. But as you are working with slices/arrays already there is no need to go with iterators – Dawer May 23 '21 at 16:58
  • 2
    @Dawer performance should be measured to know for certain, but I upvoted your answer since its the most clear and straight-forward way of achieving what OP wants and is more likely to be performant. – kmdreko May 23 '21 at 17:20
  • @kmdreko Thanks a lot for your insightful help as well! – Ziva May 23 '21 at 19:09

2 Answers2

2
fn combine(s: Option<&[u8; 32]>, k: &[u8; 32]) -> [u8; 64] {
    let tmps = s.unwrap();
    let mut result = [0; 64];
    let (left, right) = result.split_at_mut(32);
    left.copy_from_slice(&tmps[..]);
    right.copy_from_slice(&k[..]);
    result
}

slice::split_at_mut returns two mutable slices.

And from slice::copy_from_slice(&mut self, src: &[T]) docs:

Copies all elements from src into self, using a memcpy.

Resulting asm (GodBolt.org) is easily vectorized:

example::combine:
        push    rax
        test    rsi, rsi
        je      .LBB0_1
        movups  xmm0, xmmword ptr [rsi]
        movups  xmm1, xmmword ptr [rsi + 16]
        movups  xmmword ptr [rdi + 16], xmm1
        movups  xmmword ptr [rdi], xmm0
        movups  xmm0, xmmword ptr [rdx]
        movups  xmm1, xmmword ptr [rdx + 16]
        movups  xmmword ptr [rdi + 32], xmm0
        movups  xmmword ptr [rdi + 48], xmm1
        mov     rax, rdi
        pop     rcx
        ret
.LBB0_1:
        lea     rdi, [rip + .L__unnamed_1]
        lea     rdx, [rip + .L__unnamed_2]
        mov     esi, 43
        call    qword ptr [rip + core::panicking::panic@GOTPCREL]
        ud2
Dawer
  • 309
  • 1
  • 6
1

Here is a solution using the arrayvec crate.

use arrayvec::ArrayVec; // 0.7.0

fn combine(s: Option<&[u8; 32]>, k: &[u8; 32]) -> [u8; 64] {
    let tmps = *s.unwrap();
    let tmpk = *k;
    let result: ArrayVec<u8, 64> = tmps.iter().chain(tmpk.iter()).copied().collect();
    result.into_inner().unwrap()
}
kmdreko
  • 42,554
  • 6
  • 57
  • 106