What is the explanation behind this rust code?


#1

I was looking at the solution to this problem that can be found here and goes as follows:
EDIT: Code here is all mangled up so I recommend visualizing it on Github on the link posted above. [update: fixed formatting —mbrubeck]

pub struct Allergies(pub usize);

#[derive(PartialEq, Debug)]
pub enum Allergen { Eggs, Peanuts, Shellfish, Strawberries, Tomatoes, Chocolate, Pollen, Cats }

impl Allergies {
    pub fn is_allergic_to(&self, allergen: &Allergen) -> bool {
        let allergens = Allergies::allergens();
        let index = allergens.iter().position(|x: &Allergen| x == allergen).unwrap();
        match self.0 & 1 << index { 0 => false, _ => true }
    }

    pub fn allergies(&self) -> Vec<Allergen> {
        Allergies::allergens().into_iter().filter(|allergen| self.is_allergic_to(allergen)).collect()
    }

    fn allergens() -> Vec<Allergen> {
        vec![Allergen::Eggs, Allergen::Peanuts, Allergen::Shellfish,
            Allergen::Strawberries, Allergen::Tomatoes, Allergen::Chocolate,
            Allergen::Pollen, Allergen::Cats]
    }
}

self holds the value passed to Allergies, right? (i.e. 1 in the case of Allergies(1).is_allergic_to(&Allergen::Eggs)
What is the magic happening around that match and the line above it? I get it has to do with indexes but thats about it.

I’m new to Rust so these are probably very simple questions, thank you for your time.


#2

Allergies is a tuple struct, which has fields with no names. As you say, self.0 is indexing into the tuple, retrieving the first (and only) field, which is a bitmask representing the allergies present: 1 indicates it is present, and 0 indicates it isn’t.

The line above the match is as follows:

allergens.iter().position(|x: &Allergen| x == allergen).unwrap();

This iterates through all of the possible allergens, and returns the first index such that the condition is satisfied: in this case, the index containing the (reference to) the Allergen passed to is_allergic_to().

The match line checks to see if the bit representing the specified Allergen is present: if it evaluates to 0 it isn’t present, anything else (represented by _) indicates otherwise. This can be rewritten more succinctly as

self.0 & 1 << index != 0

Also, Allergen should probably be marked Copy (see this for further discussion), and Allergies should probably have a u8 field, not usize - (I think) you really only want a value to be usize if it’s pointing to a location in memory.

Hope this helped!


#3

First of all, thank you, it was a very good explanation.
There is still one thing I do not understand though (and in this case it’s not specific to rust) how do the & and << bitwise operators work? How can, for example, Allergies(5) be true to Allergen::Eggs and Allergen::Shellfish ?