What's the closest I can get to discriminating an enum by a char?

前端 未结 2 1007
小蘑菇
小蘑菇 2021-01-19 04:37

I\'ve written this question out many times, and have finally realized that my biggest problem is that I don\'t know how I want to represent this data, and that\'s making it

相关标签:
2条回答
  • 2021-01-19 05:07

    Your strings are sentinel values; this is a common pattern in Python, but is not how things should be done in Rust: enums are what such things should be: you’re encoding the legal values in the type system.

    You could end up with something like this:

    #[derive(Clone, Copy)]
    #[repr(u8)]
    pub enum Mask {
        ChA = b'A',
        ChB = b'B',
        ChC = b'C',
        ChD = b'D',
        Tmpr = b'T',
        Batt = b'Y',
        Acc  = b'L',
    }
    
    // e.g. Mask::ChA.into() == 'A'
    impl Into<char> for Mask {
        fn into(self) -> char {
            self as u8 as char
        }
    }
    
    impl Mask {
        // e.g. Mask::from('A') == Ok(Mask::ChA)
        pub fn from(c: char) -> Result<Mask, ()> {
            match c {
                'A' => Ok(Mask::ChA),
                'B' => Ok(Mask::ChB),
                'C' => Ok(Mask::ChC),
                'D' => Ok(Mask::ChD),
                'T' => Ok(Mask::Tmpr),
                'Y' => Ok(Mask::Batt),
                'L' => Ok(Mask::Acc),
                _ => Err(()),
            }
        }
    
        // e.g. Mask::ChA.is_chan() == true
        pub fn is_chan(&self) -> bool {
            match *self {
                Mask::ChA | Mask::ChB | Mask::ChC | Mask::ChD | Mask::Tmpr | Mask::Batt => true,
                Mask::Acc => false,
            }
        }
    
        // e.g. Mask::ChD.is_major() == false
        pub fn is_major(&self) -> bool {
            match *self {
                Mask::ChA | Mask::ChB | Mask::ChC => true,
                Mask::ChD | Mask::Tmpr | Mask::Batt | Mask::Acc => false,
            }
        }
    }
    

    If you wanted you could implement std::str::FromStr for Mask as well, which would allow "A".parse() == Ok(Mask::ChA):

    impl FromStr for Mask {
        type Err = ();
    
        fn from_str(s: &str) -> Result<Mask, ()> {
            match s {
                "A" => Ok(Mask::ChA),
                "B" => Ok(Mask::ChB),
                "C" => Ok(Mask::ChC),
                "D" => Ok(Mask::ChD),
                "T" => Ok(Mask::Tmpr),
                "Y" => Ok(Mask::Batt),
                "L" => Ok(Mask::Acc),
                _ => Err(()),
            }
        }
    }
    

    I suspect that is_chan et al. may be more suitable than ADC_CHANS et al., but if you do actually need them, they work fine (you could do [Mask; 6] too, but if you need to add new elements it’d change the type which is an API compatibility break if public):

    pub static ADC_CHANS: &'static [Mask] = &[
        Mask::ChA,
        Mask::ChB,
        Mask::ChC,
        Mask::ChD,
        Mask::Tmpr,
        Mask::Batt,
    ];
    
    pub static ADC_MAJORS: &'static [Mask] = &[
        Mask::ChA,
        Mask::ChB,
        Mask::ChC,
    ];
    
    0 讨论(0)
  • 2021-01-19 05:32

    Copying a &'static str (i.e. copying the reference only) has no cost. A deep copy of the string would be a clone and would be typed as a String.

    If &'static str is too verbose for you, you can always define a type alias.

    type Str = &'static str;
    

    HashMap<char, &'static str> corresponds nicely to your original map. However, if you don't need the full range of char for the key and you don't actually need to have the value typed as a char anywhere besides indexing the map, you should use an enum instead, as that will restrict the legal values that can be used as keys.

    0 讨论(0)
提交回复
热议问题