I am writing a parser/generator for the ads-l protocol which where I'm using enums internally. So internally I store the aircraft category as enum and whenever I have to pack the data I am using the integer value. So my question is what is the best way to implement the bridge between integer and the enum?
When I implement From I have automatically the Into as written in the documentation. The problem is like visible in the example below, it could be still converted directly to an integer without using the Into trait and therefore it could lead to wrong values (If the enums are not in the correct order for example)
pub enum AircraftCategory {
Unknown,
LightFixedWing,
HeavyFixedWing,
Rotorcraft,
Sailplane,
LighterThanAir,
Ultralight,
HangAndParaglider,
Parachute,
EVTOLAndUAM,
Gyrocopter,
UASOpenCategory,
UASSpecific,
UASCertified,
}
// If you use `main()`, declare it as `pub` to see it in the output:
impl From<usize> for AircraftCategory {
fn from(value: usize) -> Self {
match value {
0 => Self::Unknown,
1 => Self::LightFixedWing,
2 => Self::HeavyFixedWing,
3 => Self::Rotorcraft,
4 => Self::Sailplane,
5 => Self::LighterThanAir,
6 => Self::Ultralight,
7 => Self::HangAndParaglider,
8 => Self::Parachute,
9 => Self::EVTOLAndUAM,
10 => Self::Gyrocopter,
11 => Self::UASOpenCategory,
12 => Self::UASSpecific,
13 => Self::UASCertified,
_ => Self::Unknown,
}
}
}
pub fn main() {
let a = AircraftCategory::Parachute as u8;
println!("{}", a)
}
But in this case I still have to implement the From trait, because I wanna convert from integer to the enum as well. In this case the numbering would be redundant which could be error prone
You could add a "dummy" variant to enum with some data (even zero-sized, like Dummy(())) - this will prevent the as cast, as it will become non-trivial. This has the cost of matching on the never-created variant, of course.
I tried the following, but it seems not to work using contants in the match. It would be elegant if it would be possible to disallow to directly converting an enum to the integer, but this is slower than using directly the int value of the enum
pub enum AircraftCategory {
Unknown = 0,
LightFixedWing = 1,
HeavyFixedWing = 2,
Rotorcraft = 3,
Sailplane = 4,
LighterThanAir = 5,
Ultralight = 6,
HangAndParaglider = 7,
Parachute = 8,
EVTOLAndUAM = 9,
Gyrocopter = 10,
UASOpenCategory = 11,
UASSpecific = 12,
UASCertified = 13,
}
// If you use `main()`, declare it as `pub` to see it in the output:
impl From<usize> for AircraftCategory {
fn from(value: usize) -> Self {
const Unknown: usize = AircraftCategory::Unknown as usize;
const LightFixedWing: usize = AircraftCategory::LightFixedWing as usize;
const HeavyFixedWing: usize = AircraftCategory::HeavyFixedWing as usize;
const Rotorcraft: usize = AircraftCategory::Rotorcraft as usize;
const Sailplane: usize = AircraftCategory::Sailplane as usize;
const Ultralight: usize = AircraftCategory::Ultralight as usize;
const HangAndParaglider: usize = AircraftCategory::HangAndParaglider as usize;
const Parachute: usize = AircraftCategory::Parachute as usize;
const EVTOLAndUAM: usize = AircraftCategory::EVTOLAndUAM as usize;
const Gyrocopter: usize = AircraftCategory::Gyrocopter as usize;
const UASOpenCategory: usize = AircraftCategory::UASOpenCategory as usize;
const UASSpecific: usize = AircraftCategory::UASSpecific as usize;
const UASCertified: usize = AircraftCategory::UASCertified as usize;
match value {
Unknown => Self::Unknown,
LightFixedWing => Self::LightFixedWing,
HeavyFixedWing => Self::HeavyFixedWing,
Rotorcraft => Self::Rotorcraft,
Sailplane => Self::Sailplane,
LighterThanAir => Self::LighterThanAir,
Ultralight => Self::Ultralight,
HangAndParaglider => Self::HangAndParaglider,
Parachute => Self::Parachute,
EVTOLAndUAM => Self::EVTOLAndUAM,
Gyrocopter => Self::Gyrocopter,
UASOpenCategory => Self::UASOpenCategory,
UASSpecific => Self::UASSpecific,
UASCertified => Self::UASCertified,
_ => Self::Unknown,
}
}
}
pub fn main() {
let a = AircraftCategory::Parachute as u8;
println!("{}", a)
}
I might suggest the match value => Self::Unknown(value), so that applications can take advantage of abnormal categories even if they aren't supported by this library. The Unknown variant would need to be specified as Unknown(usize) or similar, a well.
Alternately, if this should not be supported, this arm could signal an error - which would require that the method be able to return one, which rules out From. TryFrom or a non-trait conversion method may be more appropriate in that case.
#[derive(Debug)]
enum Test{
Unknown = 0,
A = 1,
B = 2,
C = 3,
}
impl From<usize> for Test {
fn from(value: usize) -> Self {
match value {
_ if value == Test::Unknown as usize => Test::Unknown,
_ if value == Test::A as usize => Test::A,
_ if value == Test::B as usize => Test::B,
_ if value == Test::C as usize => Test::C,
_ => Test::Unknown,
}
}
}
pub fn main() {
let t = Test::A;
let ti = t as u32;
println!("{}", ti);
let t2: Test = 2.into();
println!("Test::{:?}", t2);
}