Hi Omar, great write-up! Just a hint for your enum to integer value conversion you can do this with simply type-casting using the repr attribute. This would shorten your code where you pass an enum variant to the MAX7219, because there is no need to match against all the enum variants. Illustrative example:
Another thing I'd improve, is in your matches against enumerations that are exhaustive, e.g. DigitRowAddress, most of the time it's better to not match against _ for any variant, as first of all it cannot happen because you covered all cases already (and the chip wont add new pins during its lifetime) - and second: if somebody added Digit8 to the DigitRowAddress enum, your code keeps happily compiling even though your match block is now broken. At runtime it will error out, but it could already error at compile time which is preferred, especially on embedded systems without visual feedback. The type system of Rust allows many of those compile time sanity verifications and makes embedded programming a joy. Aiming to only allow valid states at compile time basically made me stop using debuggers :)
Omar Hiari You might want to look into num_enum:: TryFromPrimitive derive macro too, this takes away the manual implementation of the TryFrom trait for going from integer to enum. I am looking forward to the published MAX7219 crate :-)
Omar Hiari you're welcome! No alternative is needed, simply change your Cargo.toml to not use the default features for this crate, it is no_std compatible, see their documentation. Two options:
you either use the amazing cargo-edit and run cargo add num_enum --no-default-features
or add this into your Cargo.toml num_enum = { version = "0.5.7", default-features = false }
Hi Omar, great write-up! Just a hint for your enum to integer value conversion you can do this with simply type-casting using the repr attribute. This would shorten your code where you pass an enum variant to the MAX7219, because there is no need to match against all the enum variants. Illustrative example:
// C style represenation of enum in memory, here u8 #[repr(u8)] enum DigitRowAddress { Digit0 = 0x01, Digit1 = 0x02, Digit2 = 0x03, Digit3 = 0x04, Digit4 = 0x05, Digit5 = 0x06, Digit6 = 0x07, Digit7 = 0x08, } fn draw_row_or_digit( per: MaxSpi, cs: &mut Pin<'A', 6_u8, Output>, digit_addr: DigitRowAddress, led_data: u8, ) -> Result<(), AddressError> { let send_array: [u8; 2] = [digit_addr as u8, led_data]; transmit_raw_data(&send_array, per, cs).unwrap(); Ok(()) }Another thing I'd improve, is in your matches against enumerations that are exhaustive, e.g. DigitRowAddress, most of the time it's better to not match against
_for any variant, as first of all it cannot happen because you covered all cases already (and the chip wont add new pins during its lifetime) - and second: if somebody added Digit8 to the DigitRowAddress enum, your code keeps happily compiling even though your match block is now broken. At runtime it will error out, but it could already error at compile time which is preferred, especially on embedded systems without visual feedback. The type system of Rust allows many of those compile time sanity verifications and makes embedded programming a joy. Aiming to only allow valid states at compile time basically made me stop using debuggers :)