No history yet

Structs and Enums

Transcript

Beau

Okay, so we've covered a lot of the, like, the building blocks. Variables, functions, loops... the core stuff. But it all feels a bit... separate? You know? We have a string for a name, a number for an age, a boolean for... I don't know, whether my coffee is ready.

Jo

Right. They're all individual pieces of data. You've got the Lego bricks, but you haven't built anything with them yet.

Beau

Exactly! Like, in the real world, you don't just have a 'username'. You have a 'user'. And a user *has* a username, and an email, and an age, and maybe a sign-up date... all that stuff belongs together.

Jo

And that is the perfect lead-in to one of Rust's most fundamental features for creating your own types: structs.

Beau

Structs. Okay, sounds... structural. Is it like a blueprint?

Jo

That's exactly what it is. You use the 'struct' keyword to define a blueprint for a piece of data. So, for your user example, you'd write 'struct User', then use curly braces. Inside, you define the fields. So, 'username: String', 'email: String', and maybe 'active: bool'.

Beau

So you're creating a new custom data type, called 'User', that groups those three things together.

Jo

Precisely. The struct definition is the blueprint. Then, to actually create a user, you create an *instance* of that struct. You'd say something like 'let user1 = User { ... }' and then you fill in the values for username, email, and active.

Beau

Okay, give me the mental movie for that. The blueprint versus the instance.

Jo

Imagine a blank character sheet for a game. The 'struct User' is that blank sheet. It has empty slots for 'Name', 'Class', 'Hit Points'. That's the blueprint. Creating an instance is you taking a sheet and filling it out: 'Name: Beau', 'Class: Podcaster', 'Hit Points: 10'. You can create another instance by grabbing another blank sheet and filling it out for me. Two different characters, but they both follow the same structure.

Beau

I like that. So once I have my filled-out character sheet, my... uh... 'user1' variable, how do I get the email back out?

Jo

You just use dot notation. So, 'user1.email' would give you the email string. 'user1.active' would give you the boolean. It's very direct.

Beau

Okay, that makes sense. But can a User *do* anything? Or is it just a bucket for data?

Jo

Excellent question. Yes, they can. You can define functions that are specifically associated with a struct. In Rust, these are called methods.

Beau

Methods. So like... a 'deactivate_user' function that only works on a User struct?

Jo

Exactly. You create what's called an 'impl' block, which stands for implementation. You'd write 'impl User' and inside that block, you define your functions. The first parameter of almost any method is '&self', which is a reference to the instance of the struct the method is being called on.

Beau

So if I call 'user1.deactivate()', inside that function, 'self' basically means 'user1'.

Jo

You got it. So a method like that could change the 'active' field on 'self' from 'true' to 'false'. It lets you bundle behavior together with the data it operates on. It's incredibly powerful for organization.

Beau

Okay, I'm with you on structs. But what if you have a piece of data that can be one of several... things? Like, a user's status isn't just active or inactive. It could be 'Active', 'PendingVerification', 'Suspended', 'Banned'. It can only be *one* of those at a time.

Jo

That's where the other major custom type comes in: enums. It's short for enumeration. An enum lets you define a type by listing, or enumerating, its possible *variants*.

Beau

So I would create an 'enum UserStatus' and then list... 'Active', 'Pending', 'Suspended'. And a variable of type 'UserStatus' could hold *only* one of those values? Nothing else?

Jo

Exactly. It makes your code much safer. You can't accidentally assign it a string that says 'sort of active'. The compiler guarantees it's one of the variants you defined. But here's where Rust enums get really interesting. The variants can hold data.

Beau

What do you mean?

Jo

Well, an 'Active' status doesn't need any extra info. But a 'Suspended' status probably needs a reason, right? Like a string explaining why. You can define the enum variant as 'Suspended(String)'.

Beau

Whoa. So the type of data it holds can change depending on the variant?

Jo

Yes. The 'Active' variant holds nothing, but the 'Suspended' variant holds a 'String'. This lets you model states very precisely. An 'Active' state has no associated data, but a 'Suspended' state *must* have a reason attached.

Beau

Okay, that's genuinely cool. But... how do you work with that? If you have a 'UserStatus' variable, how do you know which variant it is and, if it's 'Suspended', how do you get the reason string out?

Jo

This brings us back to something we touched on briefly before: the 'match' expression. It's like a super-powered 'if' statement, and it's built perfectly for enums. You write 'match status {'... and then you provide a line of code, an 'arm', for every single possible variant.

Beau

Every single one? What happens if you forget one?

Jo

The compiler gives you an error. It won't let you build your program. Rust forces you to handle every possible case, which is called exhaustive matching. It prevents a huge category of bugs where you forget to handle a specific state.

Beau

So for our status enum, I'd have a line for 'Active', a line for 'Pending', and a line for 'Suspended'...

Jo

Correct. And for the 'Suspended' arm, you'd write 'UserStatus::Suspended(reason) => { ... }'. That 'reason' is a new variable that gets created right there, and inside those curly braces, you have access to the string that was stored inside the enum. It unpacks the data for you.

Beau

So structs group related data together, like a user profile. Enums define a type that can be one of a few different states, and those states can even carry different data. And 'match' is how you safely check which state you're in and use that data.

Jo

That's a perfect summary. These two constructs, structs and enums, are the absolute bedrock of building complex applications in Rust. Pretty much everything you model will be a combination of these two.