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

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,
];

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.

Tags:

Enums

Rust