A Rust Feature I Like


I would like to talk for a little bit about why I like Rust, but I want to do so in concrete terms, not just handwaving at bullet points or arguing about marketing.


I am writing a game. In this game, I have dialog boxes that can pop up with messages to the player. So, I have a struct to represent a dialog box:

#[derive(Clone, Debug)]
pub struct Modal {
    pub size: Vector2<i32>,
    pub contents: Vec<ContentType>,
    pub dismiss: DismissType
}

Straightforward. A dialog consists of a size (I'll center them on the screen so I don't care about location), a list of what's in the dialog, and you have a way to dismiss a dialog once you've read it (it also has some boilerplate behavior I can define for it, it can be cloned and it can be printed as debug output).

So let's think first about the contents of a dialog. In C++ or Java, that would be some kind of base DialogContent class that would have a virtual (or abstract) method for displaying it on the screen. If my dialogs have titles, text blocks, and maybe images, that's four classes (base and inherited). If I'm writing something to talk to this, how do I know those four classes exist? The (hypothetical cpp) interface of Modal doesn't tell me; it just tells me I want "something that inherits from ContentType" (or "something that implements ContentType" in Java).

In Rust, ContentType is an enum. "But wait," you say, "C++ also has enums, so what?" C++'s enums are some names given to normal ints. Rust's enums have superpowers:

#[derive(Clone, Debug)]
pub enum ContentType {
    Center(String),
    Text(String),
    CenterSprite(Sprite),
}

First thing is that these enums aren't ints, they're variants of a type, and the type is something-like-a-union. Depending on which variant you have there can be data associated with it. So, for example, here's a perfectly valid ContentType:

ContentType::Center( String::from("Dialog Title") )

"A ContentType of Center, containing the string 'Dialog Title'". You don't have to specify data along with them. either, and they don't all have to be the same:

#[derive(Clone, Debug, PartialEq)]
pub enum DismissType {
    Any,
    Letter(String),
}

The two variants here mean that a given dialog can be dismissed by pressing any key, or only by pressing one of the chars in the given string. And this is a good lead-in to another superpower Rust enums have: the match statement:

match modal.dismiss {
    Any => self.world.despawn(entity).unwrap(),
    Letter(ch) => {
        // something with ch here
    }
}

If I add a variant to the DismissType enum and I forget to update that match statement, it won't compile, so I can be sure that every case is covered (you can use wildcards to catch every case in times when you don't care to enumerate all of them).

Also, being types, I can implement methods on enums:

pub enum Items {
    HealthPotion,
    // etc...
}

impl Items {
    pub fn sprite(&self) -> Sprite {
        use Items::*;
        match self {
            HealthPotion => Sprite::new((0, 0), (16, 16)).with_layer(5),
            // etc...
        }
    }
}

Now I can pick any instance of this enum and ask it what sprite it represents. Add an Item and the compiler will catch that and require me to specify a sprite for it, to make the match exhaustive.


Anyway, that's one of the things I like about Rust: enums. They're a powerful tool I can use that other languages don't give me.