The Rust Programming Language

Enums & Pattern Matching

Richard Feldman

Richard Feldman

Vendr, Inc.
The Rust Programming Language

Check out a free preview of the full The Rust Programming Language course

The "Enums & Pattern Matching" Lesson is part of the full, The Rust Programming Language course featured in this preview video. Here's what you'd learn in this lesson:

Richard demonstrates how to define enums in Rust, set payloads for variants, obtain values using pattern matching, and use pattern matching as an if statement. Rust has no concept of null or undefined, therefore, if pattern matching is used like an if statement every variant must be covered or a compiler error will be thrown.


Transcript from the "Enums & Pattern Matching" Lesson

>> Part Three, Pattern Matching. So, we're gonna talk about a few things in this section, enums, pattern matching itself, methods, and type parameters. Let's start with enums, this is an enum and rust. Enum is short for enumeration, although if you've encountered enumerations in other languages, they're probably not quite what you're expecting when it comes to how they work in rust.

Here's an example of how to make one of these, I'm gonna say let go equals color green. Let stop equals color red, and let's slow down equals color yellow. So these are like American traffic light colors. So we're doing here is we're saying that color is enumeration of three different possible values.

So here I've said I've assigned the variable go to be the green variant, stop to be the red variant, and slow down to be the yellow variant. Now, in all three of these cases, the type of the value is color, but they have different values inside of them.

So much like how if I were to make a string, I could have different values of strings, some strings have different contents than others. Or, if I were to make a number, there are different numbers, values that can go inside the same number type. It's the same thing with enums, all three of these have the same type, namely color, but they have different values at runtime, in this case, green, in this case, red in this case, yellow.

So the type is what goes after the word enum, and then the things inside of the curly braces are variants. And the way that you instantiate them is you sort of namespace them with the type followed by colon, colon, and then the name of the variant. So you don't just say green or red or yellow, you say color colon colon green, or color colon colon red.

And that's just so that in case you have multiple enums, in the same scope, with overlapping variants. Like let's say, I had another thing that had something and it's called red, I wouldn't want to get those mixed up. And so this way I can distinguish between color, colon, colon red, and whatever my other thing is that also has a red variance.

We can also add additional variants that have data inside of them. So here we have another variant called custom, and I've actually included some data that's that's known as a payload within this variant. So here, this is basically using normal struct syntax, so I've set it red, green, and blue.

This is basically like okay, let's say I don't wanna have one of these normal colors, I wanna have a custom color with red, green, and blue each being u8. So I could once again say go is green, stop is red, but now maybe I wanna just have a color called purple.

And to do this I would say color colon colon custom. And then inside of this this would be normal struct syntax like red 100, green 0, blue 250. And so this would be the way that I would define a one of these color custom variants. Also, another way you can do this is you can have it with tuples instead of structs.

So here's an example of that, and so I say color:: custom and just parentheses, 100, 0, 250. Unlike regular tuples if you want, you can actually have a tuple with one element here, cuz that actually is useful in this case. But basically, either of these do exactly the same thing, cuz like we saw in the previous section, tuples and structs, basically have the same feature set.

It's just a matter of whether you want them to be labeled or done by position. Also worth noting, you can have as many of your variants use payloads as you want, or none of them. If you want, you can say, none of my variants are gonna have payloads, or you could say all of my variants have payloads, or you could mix and match like we've done here, it's totally up to you.

Okay, so how do we get values out of these variants once we've constructed them? Well, that's where we're gonna do pattern matching. So let's say I've said let current color equals color yellow. So currently I'm looking at the color yellow, but current color could have been set to anything, or it could even be a function argument.

And now, I'm gonna run a match on this. So this match keyword works a little bit like switch in other languages like Java Script, but there are also some differences. So basically what this is saying, is I give it a value, and it says, okay, I'm going to do what's called a pattern match on each of these.

And I'm basically going to go and try each of these different branches one at a time, until I find one that matches current color. So the first branch that it tries is the color colon, colon green branch. So basically it's saying, is current color equal to color colon colon green?

Well, it's not, so it says okay, I'm gonna skip over this entire block, and move down to the next one. This one, it's again gonna see that this matches as, it does match, so great, we're gonna run this branch. Now, where are these differ from switch statements that you might have seen in other languages is a couple of ways.

Number one, with switch statements you usually have to put something like break after each one, to prevent things from falling through. That's not a thing here, you don't need to say break because there is no fall through. The other big difference is that usually when your saying switch your comparing two, like a number or maybe a string depending on the language.

But as we'll see, you can do actually a good bit more with this pattern matching. So, here's an example of something more involved, where we've got the color custom that actually has the payload in there. So here, we've got the same pattern match for green, same pattern match for yellow.

And here, we're doing two things at once. One, we're saying, okay, if this variant is a custom variant, then I wanna run this code, but also what we're saying, is we're doing a struct destructure in here, to give names to each of the values inside that custom struct.

So basically we're saying, not only do I want to run this code if it's a custom variant, but also I wanna pull out these red, green, and blue values. And then now those are just variables that only exist within these curly braces in terms of scope. So I can print line there red, green, or blue, or do whatever else I want with them.

And it'll be basically the same thing if we use the the tuple style rather than the struct style, except instead of being curly braces, we would have the tuples. But otherwise, it's exactly the same. Much like with IF, you can use match in the same way as an expression if you like to.

So once again, we're seeing the same thing that we saw with IF, where if you're gonna say match on this thing, you can set it equal to something if you if you like, but then you do need to have that semicolon at the end. But, worth noting, that if you try to do this, you and assign it to be something, you actually do need to include every single possible variant, or at least have some coverage for each of those.

Because otherwise, rust won't know what to set color stir to be equal to. So if you did this, it would say, this is the compiler error you would get, it would say patterns red and customer not covered. In other words, if rust runs this code, and current color is equal to green or yellow, okay, great it knows what to do, to set color stir to be equal to.

But if it does not encounter, either green or yellow, if it encounters red or custom, it doesn't know what to do, it doesn't know what to set color stir to. And remember, rust doesn't have a concept like null or undefined. Instead, rust just has something we'll see in a second.

But basically, since it wouldn't know how to set that, it's gonna give you a compiler error and say, you know what, I need to know in all possible situations what to do here, so that no matter what I have something to set color stir to. Here's one way you can do that, this is what's called a catch all pattern, it's basically an underscore.

And this basically says, yeah, this just matches anything. I don't care what I've got, if it didn't match one of the previous patterns, then I wanna run this branch right here. Which is totally fine, that's a reasonable way to go about doing it. But, worth noting that there is a downside if you do this.

And the downside is that this error message is actually potentially quite helpful when you introduce a new variant, because it will immediately pop up in all of your matches that are doing this style of matching, if that don't have the underscore. It'll say, hey, you've forgot to incorporate it here, you've forgot to incorporate it here, you've forgot to incorporate it here.

But, if you do the catch all, now you have this situation where whenever you add a new variant here, there's not gonna be any error, which means you're not gonna get a reminder to maybe go and try to handle that differently, depending on what logic is there. So that's sort of a trade off.

You can feel free to use this underscore, this default case if you want to or not, but those are the trade offs.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now