3
\$\begingroup\$

In Rust, I want to take an array of u32 values, convert each to four bytes in big endian, and concatenate them to yield a Vec<u8> result. Example:

let input: [u32; 5] = [
 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0];
(... Do some data conversion work ...)
let output: Vec<u8> = vec![
 0x67, 0x45, 0x23, 0x01,
 0xEF, 0xCD, 0xAB, 0x89,
 0x98, 0xBA, 0xDC, 0xFE,
 0x10, 0x32, 0x54, 0x76,
 0xC3, 0xD2, 0xE1, 0xF0,
];

I have several pieces of working code that all give the same answer.

Algorithm A (for-loop):

let mut output = Vec::<u8>::new();
for val in &input{
 output.extend_from_slice(&val.to_be_bytes());
}

Algorithm B (for-each):

let mut output = Vec::<u8>::new();
input.iter().for_each(|val| output.extend_from_slice(&val.to_be_bytes()));

Algorithm C (fold-append):

let output = input.iter().fold(Vec::<u8>::new(),
 |mut acc, val| {acc.extend_from_slice(&val.to_be_bytes()); acc});

Algorithm D (flat-map):

let output: Vec<u8> = input.iter().flat_map(|val| val.to_be_bytes().to_vec()).collect();

Is there an even shorter, clearer way to accomplish the task? Maybe algorithm D's to_vec() could be avoided somehow?

Speed is not a concern because this code only executes a few times, and the input array length is fixed at 5.

Toby Speight
87.3k14 gold badges104 silver badges322 bronze badges
asked Oct 7, 2020 at 3:25
\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

Algorithm A seems the clearest to me. An improvement might be to use Vec::with_capacity to avoid the allocation. Indeed, arrays in Rust are currently somewhat cumbersome to use.

My advice would be to wrap it in a function and not worry about it later on:

pub fn to_bytes(input: &[u32]) -> Vec<u8> {
 let mut bytes = Vec::with_capacity(4 * input.len());
 for value in input {
 bytes.extend(&value.to_be_bytes());
 }
 bytes
}
#[cfg(test)]
mod tests {
 #[test]
 fn to_bytes() {
 use super::*;
 assert_eq!(to_bytes(&[]), &[]);
 let input = &[0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0];
 let output = &[
 0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, 0x89, 0x98, 0xBA, 0xDC, 0xFE, 0x10, 0x32,
 0x54, 0x76, 0xC3, 0xD2, 0xE1, 0xF0,
 ];
 assert_eq!(to_bytes(input), output);
 }
}

(playground)

answered Oct 7, 2020 at 12:32
\$\endgroup\$
3
\$\begingroup\$

Rust 1.53.0 (2021年06月17日) introduces the IntoIterator for array types, which finally makes this shorter code possible:

Algorithm E (flat-map simpler):

let output: Vec<u8> = input.iter().flat_map(|val| val.to_be_bytes()).collect();

(This is based on my algorithm D, removing .to_vec(). I did check that algorithm E fails to compile using Rust 1.52.0.)


Algorithm F (not recommended, Rust 1.51.0, see std::array::IntoIter::new()):

let output: Vec<u8> = input.iter().flat_map(|val|
 std::array::IntoIter::new(val.to_be_bytes())).collect();
answered Jul 11, 2021 at 15:46
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.