I have a function in an embedded system which translates an image encoded in a 32-bit integer to 3x9 matrix (an array of arrays):
pub fn image_to_preformated_vector(image: u32) -> [[u8; 9]; 3] {
[
[
1 & (image >> 0) as u8,
1 & (image >> 1) as u8,
1 & (image >> 2) as u8,
1 & (image >> 3) as u8,
1 & (image >> 4) as u8,
1 & (image >> 5) as u8,
1 & (image >> 6) as u8,
1 & (image >> 7) as u8,
1 & (image >> 8) as u8,
],
[
1 & (image >> 9) as u8,
1 & (image >> 10) as u8,
1 & (image >> 11) as u8,
1 & (image >> 12) as u8,
1 & (image >> 13) as u8,
1 & (image >> 14) as u8,
1 & (image >> 15) as u8,
1 & (image >> 16) as u8,
1 & (image >> 17) as u8,
],
[
1 & (image >> 18) as u8,
1 & (image >> 19) as u8,
1 & (image >> 20) as u8,
1 & (image >> 21) as u8,
1 & (image >> 22) as u8,
1 & (image >> 23) as u8,
1 & (image >> 24) as u8,
1 & (image >> 25) as u8,
1 & (image >> 26) as u8,
],
]
}
Alternative implementation
pub fn image_to_preformated_vector(image: u32) -> [[u8; 9]; 3] {
[
[0, 1, 2, 3, 4, 5, 6, 7, 8],
[9, 10, 11, 12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23, 24, 25, 26],
]
.map(|v| v.map(|i| 1_u8 & (image >> i) as u8))
}
Which is the preferred implementation, and why? Is there an even better way of writing it?
-
\$\begingroup\$ I was not able to use a range without a mutable temporary variable. \$\endgroup\$Simson– Simson2025年05月14日 06:39:21 +00:00Commented May 14 at 6:39
-
2\$\begingroup\$ Did you profile? The second snippet is clearly way more readable, but the former might be faster. \$\endgroup\$STerliakov– STerliakov2025年05月14日 15:38:55 +00:00Commented May 14 at 15:38
-
1\$\begingroup\$ The execution time for this snippet is not critical, but readability and maintainability is. \$\endgroup\$Simson– Simson2025年05月15日 07:16:38 +00:00Commented May 15 at 7:16
2 Answers 2
You could use macros to write your function in a manner which works like your first version, but with an improved readability. Something such as:
pub fn image_to_preformated_vector(image: u32) -> [[u8; 9]; 3] {
macro_rules! shift {
($idx:expr) => { 1 & (image >> $idx) as u8 };
}
macro_rules! row {
($idx:literal) => {
[
shift!(0 + $idx), shift!(1 + $idx), shift!(2 + $idx),
shift!(3 + $idx), shift!(4 + $idx), shift!(5 + $idx),
shift!(6 + $idx), shift!(7 + $idx), shift!(8 + $idx),
]
};
}
[ row!(0), row!(9), row!(18), ]
}
I'd raise the question whether you really need to convert the image.
If you need access on the matrix's elements, you can implement an appropriate associated function for an image new type, since a u32
obviously requires less storage than a [[u8; 9]; 3]
.
So if and when you need to access the respective matrix's fields, you can use a constant and rather cheapTM on the fly operation like so:
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Image(u32);
impl Image {
pub const fn get(&self, outer: u8, inner: u8) -> u8 {
1 & (self.0 >> (outer * 9) + inner) as u8
}
}
Of course you may want to implement some additional guards to limit the indices to valid values.
If you use a library for matrix operations, you can implement its respective traits for the Image(u32)
new type and treat it as a matrix.