#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Allergen {
Eggs = 1 << 0,
Peanuts = 1 << 1,
Shellfish = 1 << 2,
Strawberries = 1 << 3,
Tomatoes = 1 << 4,
Chocolate = 1 << 5,
Pollen = 1 << 6,
Cats = 1 << 7,
}
use self::Allergen::*;
const ALLERGENS: [Allergen; 8] = [
Eggs,
Peanuts,
Shellfish,
Strawberries,
Tomatoes,
Chocolate,
Pollen,
Cats,
];
pub struct Allergies {
allergens: u32,
}
impl Allergies {
pub fn new(n: u32) -> Allergies {
Allergies { allergens: n }
}
pub fn is_allergic_to(&self, allergen: &Allergen) -> bool {
self.allergens & *allergen as u32 != 0
}
pub fn allergies(&self) -> Vec<Allergen> {
ALLERGENS
.iter()
.filter(|a| self.is_allergic_to(a))
.cloned()
.collect()
}
}
This approach starts by defining the Allergen
enum.
Since the values of the Allergen
enum are primitive numeric types, the Copy
trait is derived when defining the enum.
Clone
is a supertrait of Copy
, so everything which is Copy
must also implement Clone
, so Clone
is derived as well.
The use self::Allergen::*;
line is so that the Allergen
values can be referred to directly in the file,
instead of needing to prefix every value with Allergen
, e.g. 'Allergen::Eggs`.
A fixed-size array is defined to hold the Allergen
values as a way to iterate the enum values without requiring an external crate.
The [Allergen; 8]
is used to give the type and length of the array.
To be a const
, the size of the array must be known at compile time, so setting the type and length must be done explicitly,
so the size in bytes of the fixed array can be deduced by the compiler from that and not by inspecting the element types and counting
the elements itself.
The Allergies
struct is defined with its allergens
field given the type of u32
.
Next, the methods for the Allergies
struct are implemented.
The new()
method sets its allergens
field to the u232
value passed in.
The is_allergic_to()
method uses the bitwise AND operator (&
) to compare the Allergen
passed in with the allergens
u32
field.
The dereferenced Allergen
passed in is cast to a u32
for the purpose of comparison with the allergens
u32
value.
The method returns if the comparison is not 0
.
If the comparison is not 0
, then the allergens
field contains the value of the Allergen
, and true
is returned.
For example, if the allergens
field is decimal 3
, it is binary 11
.
If the passed-in Allergen
is Peanuts
, which is the value 2
decimal, 10
binary, then 10
ANDed with 11
is 10
, not 0
,
and so the method would return true
.
If the passed-in Allergen
is Shellfish
, which is the value 4
decimal, 100
binary, then 100
ANDed with 011
is 0
,
and so the method would return false
.
The allergies()
method iterates through the ALLERGENS
array, passing each element of the array to the filter()
method.
The closure (also known as a lambda), returns the result of passing the Allergen
to the is_allergic_to()
method.
The elements that survive the test of being present in the allergens
field are passed to the cloned()
method. which
converts the iterator of references to Allergen
values to an iterator of copies of the Allergen
values.
Since the Allergen
values derive from Copy
, they are inexpensively copied.
The cloned elements are chained to the collect()
method, which uses type inference to collect
the elements into a Vec<Allergen>
as defined in the method signature for the return value.