Based on the @ker suggestion, portable macros are presented here that create persistent identifiers based on fixed-size byte strings:
Warning: there are some restrictions on these constants that are not immediately obvious (see notes below).
Support for the following macros:
const MY_ID: u16 = u16_code!(b"ID"); const MY_WORD: u32 = u32_code!(b"WORD"); const MY_LONG: u64 = u64_code!(b"LONGWORD");
Implementation:
#[cfg(target_endian = "little")] #[macro_export] macro_rules! u16_code { ($w:expr) => { ((($w[0] as u16) << 0) | (($w[1] as u16) << 8) | ((*$w as [u8; 2])[0] as u16 * 0)) } } #[cfg(target_endian = "big")] #[macro_export] macro_rules! u16_code { ($w:expr) => { ((($w[1] as u16) << 0) | (($w[0] as u16) << 8) | ((*$w as [u8; 2])[0] as u16 * 0)) } } #[cfg(target_endian = "little")] #[macro_export] macro_rules! u32_code { ($w:expr) => { ((($w[0] as u32) << 0) | (($w[1] as u32) << 8) | (($w[2] as u32) << 16) | (($w[3] as u32) << 24) | ((*$w as [u8; 4])[0] as u32 * 0)) } } #[cfg(target_endian = "big")] #[macro_export] macro_rules! u32_code { ($w:expr) => { ((($w[3] as u32) << 0) | (($w[2] as u32) << 8) | (($w[1] as u32) << 16) | (($w[0] as u32) << 24) | ((*$w as [u8; 4])[0] as u32 * 0)) } } #[cfg(target_endian = "little")] #[macro_export] macro_rules! u64_code { ($w:expr) => { ((($w[0] as u64) << 0) | (($w[1] as u64) << 8) | (($w[2] as u64) << 16) | (($w[3] as u64) << 24) | (($w[4] as u64) << 32) | (($w[5] as u64) << 40) | (($w[6] as u64) << 48) | (($w[7] as u64) << 56) | ((*$w as [u8; 8])[0] as u64 * 0)) } } #[cfg(target_endian = "big")] #[macro_export] macro_rules! u64_code { ($w:expr) => { ((($w[7] as u64) << 0) | (($w[6] as u64) << 8) | (($w[5] as u64) << 16) | (($w[4] as u64) << 24) | (($w[3] as u64) << 32) | (($w[2] as u64) << 40) | (($w[1] as u64) << 48) | (($w[0] as u64) << 56) | ((*$w as [u8; 8])[0] as u64 * 0)) } }
Note 1) a string that checks the size needed for or'd with a constant, because individual statements are not supported in constant expressions ( E0016
).
I would also prefer to use if cfg!(target_endian = "big")
inside one macro, but the same restriction on constants prevents it.
Note 2) . There is a potential problem with using these macros for inconsistent input, where an argument can be created for each byte (and possibly a health check for size). I looked at the purpose of the variable, but it also causes error E0016
.
Note 3) While Rust allows you to declare these values โโas const
, they cannot be used in match
statements.
eg:
error[E0080]: constant evaluation error --> src/mod.rs:112:23 | 112 | const MY_DATA: u32 = u32_code!(b"DATA"); | ^^^^^^^^^^^^^^^^^^ the index operation on const values is unstable | note: for pattern here --> src/mod.rs:224:13 | 224 | MY_DATA => { | ^^^^^^^
source share