Dig Deeper
Create Vec on new
Create Vec on new()
pub struct Allergies {
allergens: Vec<Allergen>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Allergen {
Eggs = 1,
Peanuts = 2,
Shellfish = 4,
Strawberries = 8,
Tomatoes = 16,
Chocolate = 32,
Pollen = 64,
Cats = 128,
}
use self::Allergen::*;
const ALLERGENS: [Allergen; 8] = [
Eggs,
Peanuts,
Shellfish,
Strawberries,
Tomatoes,
Chocolate,
Pollen,
Cats,
];
impl Allergies {
pub fn new(score: u32) -> Self {
Self {
allergens: Self::calculate_allergens(score),
}
}
pub fn is_allergic_to(&self, allergen: &Allergen) -> bool {
self.allergens.iter().any(|allergy| allergy == allergen)
}
pub fn allergies(&self) -> Vec<Allergen> {
self.allergens.clone()
}
fn calculate_allergens(score: u32) -> Vec<Allergen> {
let mut allergens = Vec::<Allergen>::new();
for flag in 0..=7 {
if score & 1 << flag != 0 {
allergens.push(ALLERGENS[flag as usize]);
}
}
allergens
}
}
This approach starts by defining the Allergies struct with an allergens field of Vec<Allergens> type.
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 in order, as enum values are not ordered to be acccessible by index.
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.
Next, the methods for the Allergies struct are implemented.
The new() method uses the calculate_allergens() method to set the allergens field for a new Allergies struct.
The is_allergic_to() method uses the any() method to see if the passed-in Allergen is contained in the allergens
field, which is a Vec of Allergen values.
The allergies() method uses the clone() method to return a copy of the allergens Vec.
Since the Allergen values derive from Copy, they are inexpensively copied.
The calculate_allergens() method uses for and range to iterate through the indexes of the ALLERGENS array.
The bitwise AND operator (&) is used to compare the score with 1 shifted left (<<) for the value of the flag.
So, if flag is 0, then 1 is shifted left 0 places, and the score is compared with bitwise 1, which is also decimal 1.
If the comparison is not 0, then the score contains the Allergen, and the ALLERGEN at the index of the flag value (Eggs) is pushed
to the output Vec.
if flag is 7, then 1 is shifted left 7 places, and the score is compared with bitwise 10000000, which is decimal 128.
If the comparison is not 0, then the score contains the Allergen, and the ALLERGEN at the index of the flag value (Cats) is pushed
to the output Vec.
Create Vec when requested
Create Vec when requested
#[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.
Source: Exercism rust/allergies