Search code examples
arraysrustslice

How to make a slice from a leading element and an array?


This code seems more complex than it needs to be:

pub fn new(id_bytes: [u8; 32], ipv4: Ipv4Addr, port: u16) -> Result<RemotePeer, secp256k1::Error> {
    let serialised_pkey: Vec<u8> = [0x02].iter().chain(id_bytes.iter()).map(|x| *x).collect();
    Ok(RemotePeer {
        id: U256::from_le_bytes(id_bytes),
        pkey: PublicKey::from_slice(&serialised_pkey)?,
        ipv4,
        port
    })
}

I have the final 32 bytes of a public key, and I need to prepend it with an '0x02' before creating the public key.

Is there a better alternative to:

[0x02].iter().chain(id_bytes.iter()).map(|x| *x).collect();

I particularly dislike the .map() which only exists to silence an error about &u8 and u8 not being the same.


Solution

  • First, .map(|x| *x) has a shorter version: .copied(). However, there's ways to avoid the iterator entirely.

    Since you know the length of the array to be 32, you can avoid allocating a Vec by using an array of length 33.

    let mut serialised_pkey = [0; 33];
    serialised_pkey[0] = 0x02;
    serialised_pkey[1..].copy_from_slice(&id_bytes);
    

    If you didn't know the length, you could use Vec::extend_from_slice.

    let mut serialised_pkey = Vec::with_capacity(id_bytes.len() + 1);
    serialised_pkey.push(0x02);
    serialised_pkey.extend_from_slice(&id_bytes);
    

    If you had control over PublicKey, you could write a constructor that takes an implementer of Read. This is more flexible since it doesn't require all the bytes to be in one slice. Consider using the Read and Write traits when dealing with sequences of u8.

    use std::io::Cursor;
    let serialised_pkey = Cursor::new([0x02]).chain(Cursor::new(id_bytes));
    let public_key = PublicKey::from_reader(serialised_pkey)?;