- So far, we've seen how to define simple enums types.
Like you can see, here,
I've defined an enum type called color,
and I've just given it some variance.
When you declare an enum like this,
effectively, it's just a simple flag.
You're saying that this concept of a color
can either be in the state of red
or the state of green or the state of blue.
It's just a flag to indicate what state
the variable is currently in.
It's also possible to associate data values with variants.
Each variant can also represent a number.
So in that case, the variant is like a flag,
and if a value has that variant,
it has the associated type of data.
So in this example here, I've set a house location.
If you wanted to know somebody's house location,
where they live, the house location could either be
the number variant, in which case,
you can get back the value, an integer 32 value,
their house number, or the house location could be
that name of the house, in which case,
you could get back a string,
the name of the house that the person lives,
or the house location could be unknown.
So when you declare a house location variable,
it could hold a value like this.
It could hold the number variant, okay?
In which case, the value inside the enum
would be the house number like the number four as an i32.
We live in house number four.
I'm currently sitting in house number four in my home
or, alternatively, the house location
could have the variant name.
So the variant is like a flag
which indicates what can value it currently contains.
If the house location has the name variant,
then the current value would be a string, okay?
So the example I'm gonna show you,
let's say your house name is called cartref.
Cartref is the Welsh word for home.
So here I live in Swansea,
you see quite a lot of houses called cartref, my home,
or, alternatively, the house location for a person could be,
it could have the variant unknown,
in which case, it doesn't have
any data associated with it at the moment.
It just has the unknown flag.
So it gives you the option of saying what kind of value
you currently contain, and what is that value?
What kind of value do you currently contain
and what is that value?
So that's quite powerful,
an enum, not only says what state it's in,
but it conveys a value for that state,
a number, a string or nothing at all,
and then given the fact that your enum can contain
a particular data value, either an integer or a string
or no value at all, the next question would be, well,
how do you actually put the value
into the enum in the first place,
and how can you get the value back out again of an enum?
So let's say that we have this enum type, a house location,
it could have the state number,
in which case, it has an i32 value in inside it
or it could be in the state name,
you know, the house location name, string, or unknown.
So you could create an enum variable like this.
h1 is an house location variable,
and it is in state number,
and it holds this, saying what variant type it is,
and this is actually kind of putting the value
into it like so.
So if you wanted to imagine what h1 looks like,
h1 is a house location object.
Each enum variable will be the same size
big enough to store the largest type of value,
big enough to store either an integer or a spice.
It's a bit like a union in other languages.
So h1, basically, has state number.
Okay, so that means we know that it contains an i32.
Okay, and it contains, specifically, the value number four.
Okay.
Or you could create another house location variable h2.
Okay, so same size,
h2, but this one holds a name.
So this is like a flag to say it holds a number
or a name, okay?
So I've called it name.
So it is currently in state name.
H2 knows that it holds the a name,
a string, in other words, and that string is cartref, home.
Like that or, possibly, I could create a variant, sorry
I could create an enum variable h3 with state unknown,
in which case, the unknown state
doesn't have any associated value.
So the variant indicates the type of value that it contains,
and then it contains a value of that type.
So this variant would indicate if an enum has that variant,
then you'd know that it has a string.
You can get a string value out of it like so.
So an enum not only tells you what state it is,
but tells you a value associated with that state.
Right, so how would you test if you had a house location,
let's call it hx,
how can you actually get the value out of it?
Well, you need to test its state.
You need to basically say, is it in this state,
in which case, I'll get a number?
Or is in this state, in which case, I'll get a string?
Or is it in this state,
in which case there is no value to get?
So you use a match statement, and it looks like this.
You declare a house location variable, give it some value,
and then you say, if the value is in this state,
what it'll do is it'll say, right,
so h, my house location,
let's say that it is in state number, okay,
which means that it does have a numeric value inside it,
like my house number four.
If my h variable is in this state,
then it'll take the value inside it and put it into here.
It extracts the value into n.
n will be an i32, okay,
because that's what the house number type was.
Alternatively, it could have been the case
that my house location didn't contain a number,
but it contained the name variant, okay?
In which case, the value inside here
will be a string, cartref.
So you say, well, if h currently
has that kind of value inside it, name, yes it does,
then it'll take the value, which is a string,
and assign it into here in, okay,
so S is now gonna be a string,
and then, obviously, if my house location variable h,
if it doesn't have any value at all,
or rather if it has the unknown variant,
okay, so I've created the house location,
and I've given it the value unknown,
then in that case, there is no value associated with it.
We know that it's in state unknown,
but we know that there's no data
associated with that variant, okay?
So it's very easy to create an enum,
give it a value of a certain type, really,
and then extract the value using a match statement,
which is like an if statement, really, in disguise.
Right, so when I need to find an enum with data,
like my enum color, then the size or my enum house location,
should say, an enum variable would be big enough
to store the largest value.
So with my house location,
if the house location had state number,
then it would hold an integer.
If the enum state was name,
then the same variable would have to hold a string.
Okay, so the variable will always be big enough
to store the largest variant.
It could change its value.
So initially, my house location could hold a number.
Okay, like the number four,
and then I could reassign it a value later on,
told maybe I actually give my house a name,
in which case, I would change the value
to be a name with the string.
So the variable itself doesn't change size.
It was always big enough to store the largest variant,
depending on what value you want to put in, okay?
So if you are interested, you can actually find out
the size of the variable using the size of function.
So we've seen this before.
I'm gonna give you a little bit more technical insight
now that we've had a few more discussions.
std is a crate.
It is the...
std is a crate.
It's the standard library in Rust.
Okay, so the entire Rust standard library
is in the std crate, which is kind of like
it companies your application.
In the std crate, it defines lots and lots of modules.
So mem is a module like a namespace, okay?
So in the std crate mem is a module,
a namespace that defines related things, memory related.
In the mem module,
there is a function called size of, the size of function
in the mem module in the std crate,
and size of is a generic function.
When you call the generic,
when you call the size of function,
you need to give it a type name.
and then it'll tell you how big it is.
So in Rust, when you call a generic function,
you have double colon and then you have the type name
that you wanna specify, okay?
So that's how you can call the generic function.
You pass in the type name, house location,
and it'll tell you how many bites is house location.
Just a word of caution,
if you're coming from other languages, most other languages,
when you are passing or calling generic functions,
and you're telling it the type involved,
in most other languages, you don't have a colon colon here.
You would just call the function name, angle brackets,
type name, and then the function name,
but in Rust, it's different.
You have the name of the generic function,
and then you have another set of colons here,
and then the type name inside angle brackets,
and it'll tell you how big house location is.
For us, house location will be the larger
of an integer 32 and a string.
So basically, it's gonna be the size of a string
because that's the larger type.
So time for an example, let's go back
into the project lesson04_enums,
and we go to look at mytypes.rs,
which defines the house location enum, and then in main.rs,
we're gonna have a look at this function here,
demo_enum_with_data.
We'll go through the code,
and then we'll run it, and see what it says.
Okay, so here's my demo project.
I've reverted all my code to as it was originally.
mytypes is there, and mytypes for demo purposes is here.
Okay, so we could use either of those.
I think I'm going to use mytypes for demo purposes.
It's got my house location,
and it's allowing me to have unused variance, okay?
And my main code, I'm using,
I'm not using the original mytypes.
I'm using the types for demo purposes
so I don't get any warnings.
I guess I could have had a crate level macro to say
or attribute to say allow dead code at the top of my code.
That would also have worked.
Okay, so that's the function that we're gonna call
demo_enum_with_data, and here it is,
and I create a house location variable,
and I say that it's a house location,
and I give it the number variant,
and in the number variant, remember,
that means you've gotta then give it an integer 32, an i32.
So I give it the number four,
that's our house location.
Here, I didn't need to actually give it a data type.
I could have just written it like that,
and that would've also been fine.
It knows that it's house location.
So then, I test my variable,
and I, basically, test the variant.
The variant is like a discriminated
to say what state are we currently in?
Oh, grab that value then.
If you're currently in the number state,
then grab the value out of it into this variable.
So this variable gets created.
n gets created here as an i32.
You live in house number four.
That's where it's gonna print at the moment.
Let's run the application in a terminal window, cargo run.
Okay, so you live in house number four.
Oh, and at the end, it says, by the way,
the size of a house location variable is,
and I've got the size of function here,
and it is 32 bytes, okay?
So, basically, that's our biggest string object is.
Right, what if I change my code here
so that instead of knowing the house location as a number,
I know it as a name, cartref.
In Welsh, an F is pronounced as a V, cartref,
a hard V, cartref, like so.
So now, it's going to match this branch or this arm,
and it'll say you live in a house named cartref.
Thank you, yes, well, not really,
but that's what it's gonna say.
Oh, right, there's a thing here.
I'll get onto this later.
When you have string literals,
you have to write it this way, string::from.
I don't wanna get involved too much about that just yet.
We need to discuss what that actually means later.
So let's just brush that one under the carpet for now,
and just we will come back and look at that later on.
So you live in a house named cartref.
Okay, very good,
and then we'll say let's change one final type to say
we live in a house.
The house location is unknown, okay?
I don't know where you live, basically.
So it'll come back.
Your house location is unknown.
Your house location is unknown,
but in every case, the variable was still the same size
because maybe what I could have done
is this variable was multiple.
Maybe, initially, it was unknown,
but then later on, I could reassign it, couldn't I?
I could say let's reassign it to be
a HouseLocation::Number(4).
Okay, so it was unknown, and, now, it's the number four.
So basically, the variable doesn't change size.
It's always big enough to hold the largest possible type
just in case you give it a different variable.
So now, it'll still be 32 bytes.
There we go.
Right, so there we are.
That's the end of this section.
Having an enum that not only tells you what state it is
but also gives you a value associated
with the current state.
This is an extremely powerful mechanism in Rust,
and for the last two sections of the lesson,
we are going to look at a couple of standard enum types
in Rust, option and result, which make use
of this kind of data bearing mechanism for enums.