An Enum is a way of expressing a [[Type]] with a finite amount of valid states ([[Sum Type]]).
For example, if we wanted to have some function that takes in a cardinal direction and prints it out as a string, you could do something like this:
```cpp
void print_cardinal(int x) {
switch x {
case 0:
std::cout << "North";
break;
case 1:
std::cout << "East";
break;
case 2:
std::cout << "South";
break;
case 3:
std::cout << "West";
break;
default:
return;
}
}
```
While this works, there are two major issues with this. The first of which is that you need to know what each $\color{lightyellow}\text{int}$ corresponds to each direction, which is impossible to know without looking *inside* the function.
A way we could rectify this is having public constants for what numbers correspond to what direction.
```cpp
const int NORTH = 0;
const int EAST = 1;
const int SOUTH = 2;
const int WEST = 3;
void print_cardinal(int x) {
switch x {
case NORTH:
std::cout << "North";
break;
case EAST:
std::cout << "East";
break;
case SOUTH:
std::cout << "South";
break;
case WEST:
std::cout << "West";
break;
default:
return;
}
}
int main() {
print_cardinal(NORTH);
print_cardinal(SOUTH);
// ...
}
```
While this works, this still isn't an ideal, because if you look at the function signature:
```cpp
void print_cardinal(int x)
```
Without reading any comments, what this function signature tells us is that this function takes in *any* integer, even though we know that isn't true. Although you can give any integer, not every integer is a valid input ($\set{0 ,1, 2 ,3}$).
An extension of [[The Goal]] that relates to this problem is being able to [[Make Invalid States Unrepresentable]]. A common way to do this is encode any constraints / [[Invariant|Invariants]] within the [[Type System]].
Looking at the problem, notice how there is nothing special about the integers $\set{0, 1, 2, 3}$, the only property we care about is that they they are distinct - and we give meaning to each number by associating it to a cardinal direction.
If we changed $\color{lightblue} \text{NORTH}$ to be $420$ instead of $0$, our
program would still work (assuming we are using the NORTH constant and not manually using 0 anywhere).
What this means is that our input, an integer that represents a cardinal direction, has *$4$* valid states:
- An *integer* that maps to $\color{lightblue}\text{NORTH}$
- An *integer* that maps to $\color{lightblue}\text{EAST}$
- An *integer* that maps to $\color{lightblue}\text{SOUTH}$
- An *integer* that maps to $\color{lightblue}\text{WEST}$
The actual *value* behind these states are only used to distinguish the different states from one another, as if two states have the same underlying *integer value*, it would be impossible to distinguish between them.
In contrast, if we look at our current function signature, the inputs we can give it are *not*
the $4$ valid $\color{lightyellow}\text{Cardinal}$ directions, but any valid $\color{lightyellow}\text{int}$.
This is where Enums come into play. Enums in C-like languages (C, [[C++]], [[Rust]], etc) allow us to define a type that is constrained to a finite number of states. Using an Enum for our `print_cardinal` implementation would look like this:
```cpp
enum class Cardinal {
NORTH,
EAST,
SOUTH,
WEST
};
void print_cardinal(Cardinal x) {
switch x {
case Cardinal::NORTH:
std::cout << "North";
break;
case Cardinal::EAST:
std::cout << "East";
break;
case Cardinal::SOUTH:
std::cout << "South";
break;
case Cardinal::WEST:
std::cout << "West";
break;
default:
return;
}
}
int main() {
print_cardinal(Cardinal::NORTH);
print_cardinal(Cardinal::SOUTH);
print_cardinal(5); // Compiler Error, cannot impliciticly cast an 'int' -> Cardinal
// ...
}
```
Using an Enum allows us to define a [[Type]] with the [[Invariant]] that a value of this Enum is one of those 4 states we defined, meaning we have changed our function signature to only be able to take an instance of a [[Validity|Valid]] instance of type $\color{lightyellow}\text{Cardinal}$ ($\color{lightblue}\text{NORTH}$, $\color{lightblue}\text{EAST}$, $\color{lightblue}\text{SOUTH}$, $\color{lightblue}\text{WEST}$).