I have a lot of repetitive code in my unit tests, which looks like this:
#[rustfmt::skip]
let bytes = [
0x00, // Byte order
0x00, 0x00, 0x00, 0x02, // LineString
0x00, 0x00, 0x00, 0x02, // Number of points
0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1.0
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2.0
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2.0
0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1.0
];
It's a concatenation of encoded primitives: u8, u32 and f64, which can be in big-endian or little-endian byte order. (For the curious: it's WKB.)
Of course, this code is not very readable or maintainable. I'd like to clean it up like this:
/// 1.0f64, big endian.
const ONE_BE: [u8; 8] = [0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
/// 2.0f64, big endian.
const TWO_BE: [u8; 8] = [0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let bytes = [
0x00, // Byte order
0x00, 0x00, 0x00, 0x02, // LineString
0x00, 0x00, 0x00, 0x02, // Number of points
...ONE_BE, ...TWO_BE,
...TWO_BE, ...ONE_BE,
];
Unfortunately, the ... syntax is only my invention, not actual Rust. I tried using a (declarative) macro instead, but a macro can only expand to a single item, not to a comma-separated sequence of items.
What's the most ergonomic way to accomplish this?
Keep in mind that I have only a small number of constants like ONE_BE and TWO_BE, but a large (and growing) number of tests that use these constants. So the less boilerplate in the actual tests, the better.
It's just test setup code, so performance is not a concern.
std::mem::transmuteit to byte slice.u32andf64is not fixed; it depends on the platform. Sotransmutewould give you a different result on, for example, x86_64 vs. ARM.(ARRAY1, ARRAY2)whereARRAY1: [u8; N]andARRAY2: [u8; M]into[u8; N+M](that is, "flatten" the arrays). I'm not sure whether this is safe, but that would not have endianness issues.