Enjoy Day 4

Now we need enum to replace "cat" and "duck" type from previous example.

Enums

You also can impl to enum 👇.

#[derive(Debug)]
enum AnimalType {
    Cat,
    Duck,
}

// How to return string or &str from enum.
impl AnimalType {
    fn as_str(&self) -> &str {
        match self {
            AnimalType::Cat => "🐈",
            AnimalType::Duck => "🐥",
        }
    }

    // How to use type as a parameters, but hey!👇 what's this? 😳
    fn say1(animal_type: AnimalType) -> &'static str {
        // To survive from fn {}, we need 👆 'static to let is has program's lifetime.
        match animal_type {
            AnimalType::Cat => "meaowww",
            AnimalType::Duck => "quackkk",
        }
    }

    // But why this didn't need to add static here? 👇 😳
    fn say2(&self, animal_type: AnimalType) -> &str {
        match animal_type {
            AnimalType::Cat => "meaowww",
            AnimalType::Duck => "quackkk",
        }
    }

    // That's because `elided lifetime rules` cover that for you already!
    // Then👇 you👇 don't need to write this loooooong👇
    fn say3<'a>(&'a self, animal_type: AnimalType) -> &'a str {
        match animal_type {
            AnimalType::Cat => "meaowww",
            AnimalType::Duck => "quackkk",
        }
    }

    // Remember this?
    fn static_say1(animal_type: &str) -> &str {
        match animal_type {
            "cat" => "meaowww",
            "duck" => "quackkk",
            _ => "wat!",
        }
    }

    // Actually the longer one look like this
    fn static_say2<'a>(animal_type: &'a str) -> &'a str {
        match animal_type {
            "cat" => "meaowww",
            "duck" => "quackkk",
            _ => "wat!",
        }
    }
}

fn main() {
    println!(
        "{0:?} aka {1:?} say {2:?}, {3:?}",
        AnimalType::Cat,
        AnimalType::Cat.as_str(),
        AnimalType::say1(AnimalType::Cat),
        AnimalType::say2(&AnimalType::Cat, AnimalType::Cat),
    );
}

💡 There's more examples about Enums, and match enums.

Don't worry about &'static str or lifetimes just yet, compiler will let you know when need (usually out of { } scope ) and we will talk about it later. Let's continue on other topics.


But I want enum string! How!

That's a great question! See below👇

strum

Install

cargo add strum
cargo add strum_macros

and get

strum = "0.26"
strum_macros = "0.26"

or

cargo add strum --features=derive

and get

strum = { version = "0.26", features = ["derive", "strum_macros"] }
use std::str::FromStr;
use strum_macros::{Display, EnumString};

#[derive(Debug, Eq, PartialEq, EnumString, Display)]
enum AnimalType {
    #[strum(serialize = "cat", to_string = "catty")]
    Cat,
    #[strum(serialize = "duck", to_string = "ducky")]
    Duck,
    Unknown,
    #[strum(disabled)]
    Pet(String),
}

#[derive(Debug, Eq, PartialEq, EnumString, Display)]
enum AnimalSound {
    #[strum(serialize = "cat", to_string = "meaowww")]
    Cat,
    #[strum(serialize = "duck", to_string = "quackkk")]
    Duck,
}

fn main() {
    // Get AnimalType from &str.
    let animal_type = AnimalType::from_str("cat");
    println!("1️⃣ animal_type: {animal_type:?}");

    // Unwrap or assign as Unknown.
    let animal_type = animal_type.unwrap_or(AnimalType::Unknown).to_string();
    println!("2️⃣ animal_type: {animal_type:?}");

    // Get AnimalSound from str.
    let cat_sound_result = AnimalSound::from_str("cat");
    println!("3️⃣ cat_sound_result: {:?}", cat_sound_result);

    // Handle cat_sound Result.
    let cat_sound_string = match cat_sound_result {
        // Handle happy case.
        Ok(animal_sound) => animal_sound.to_string(),

        // Handle error case.
        Err(err) => panic!("{:?}", err),
    };

    println!("4️⃣ cat_sound_string: {cat_sound_string:?}");

    // 😱 Uncomment this to experience an error and try to fix it by add Clone, Copy to AnimalSound
    // println!("4️⃣ cat_sound_result: {cat_sound_result:?}");

    // Match
    let animals = vec![AnimalType::Cat, AnimalType::Pet("snoopy".to_owned())];
    let my_pet = animals
        .into_iter()
        .filter_map(|e| match e {
            AnimalType::Pet(name) => Some(name),
            AnimalType::Cat => None,
            AnimalType::Duck => None,
            AnimalType::Unknown => None,
        })
        .collect::<Vec<_>>();

    println!("5️⃣ my_pet: {:?}", my_pet.join(","));
}

🤷 strum is not runnable via Rust Playground so output is shown below.

Run
1️⃣ animal_type: Ok(Cat)
2️⃣ animal_type: "catty"
3️⃣ cat_sound_result: Ok(Cat)
4️⃣ cat_sound_string: "meaowww"
5️⃣ my_pet: "snoopy"

Let's try some more complicated one!

Enums Variants

// Let's create a struct for those extra-special sushi rolls! 🍣
#[derive(Debug)]
struct SushiRollDetails {
    name: String,
    has_wasabi: bool,
    ingredients: Vec<String>,
}

// Our delightful Japanese food enum! 🍜🍣🍙
#[derive(Debug)]
enum JapaneseFood {
    // Simple and tasty ramen! 🍜
    Ramen,
    // A single piece of nigiri! 🍚
    Nigiri { topping: String },
    // A flavorful onigiri! 🍙 Holding the filling!
    Onigiri(String),
    // Our fancy sushi roll variant, holding the SushiRollDetails struct! ✨
    SpecialRoll(SushiRollDetails),
    // Delicious and crispy tempura! 🍤 Holding the type!
    Tempura(String),
}

fn main() {
    // A comforting bowl of ramen! 😊
    let dinner = JapaneseFood::Ramen;
    // Salmon nigiri! 😋
    let my_sushi = JapaneseFood::Nigiri { topping: "Salmon".to_string() };
    // A yummy tuna onigiri! 🍙
    let my_riceball = JapaneseFood::Onigiri("Tuna Mayo".to_string());
    // Our amazing special sushi roll! 🤩
    let dragon_roll = JapaneseFood::SpecialRoll(SushiRollDetails {
        name: "Dragon Roll".to_string(),
        has_wasabi: true,
        ingredients: vec!["Eel".to_string(), "Avocado".to_string(), "Cucumber".to_string()],
    });
    // Some crispy shrimp tempura! 🍤
    let fried_goodness = JapaneseFood::Tempura("Shrimp".to_string());

    // Let's see what's on the menu! 👀
    println!("Dinner: {:?}", dinner);
    println!("Sushi: {:?}", my_sushi);
    println!("Onigiri: {:?}", my_riceball);
    println!("Special Roll: {:?}", dragon_roll);
    println!("Tempura: {:?}", fried_goodness);

    // What's in our special Dragon Roll? 🐉 Let's check the details!
    match dragon_roll {
        JapaneseFood::SpecialRoll(details) => {
            println!("The {} contains: {} and has {} wasabi!",
                     details.name,
                     details.ingredients.join(", "),
                     if details.has_wasabi { "wasabi" } else { "no wasabi" });
        }
        _ => {} // Not a special roll!
    }

    // What kind of onigiri did we get? 🤔
    match my_riceball {
        JapaneseFood::Onigiri(filling) => {
            println!("Enjoying an onigiri with {} filling! 🍙", filling);
        }
        _ => {} // Not an onigiri!
    }
}

Awww, I'm hungry...


Continue to Day 5 ➠