Enums
An enum declares a closed set of named values, its cases, stored as an integer. A Direction is North, East, South or West and nothing else, and the compiler knows every case, so a match can be checked for completeness. An enum case carries no data; cases with data belong to a variant.
enum = [ attributes ] [ "pub" ] "enum" name [ ":" integer-type ] "{" [ case { "," case } ] "}"
case = name [ "=" constant-expression ]
enum Direction {
North,
East,
South,
West
}
enum Status: uint16 {
Ok = 200,
Created,
NotFound = 404,
ServerError = 500
}
Cases are separated by commas, with no comma after the last (trailing comma is not allowed in enum declarations). By convention the type and its cases are PascalCase.
Backing type and values
The integer type after the colon is the enum's backing type: every value of the enum is stored as one value of it, so sizeof(Status) is 2. It must be an integer type — enum 'Wide' base type must be an integer type otherwise. Without one, the backing type is int.
Each case has an integer value. A case may give it with =; a case without one takes the previous case's value plus one, and the first case defaults to 0:
| Case | Value |
|---|---|
Direction::North | 0 |
Direction::West | 3 |
Status::Ok | 200 |
Status::Created | 201 |
Status::NotFound | 404 |
Values may be negative when the backing type is signed:
enum Ordering: int8 {
Less = -1,
Equal,
Greater
}
Once other programs read these numbers — a file format, a network protocol, a C library — they are part of the program's interface. Inserting an unnumbered case in the middle renumbers every unnumbered case after it, so give each case whose value matters an explicit =.
Every value must fit the backing type, whether it is written with = or follows the previous case:
error: enumerator 'Size::Big' has the value 300, which the backing type 'uint8' cannot hold
help: give it a value inside the backing type's range, or declare a wider backing type
A case without = after a uint8 case of 255 has the value 256 and is refused the same way.
Naming a case
A case is named through its type: Direction::North. Inside a pattern whose subject is already known to be a Direction, the short form .North is allowed; everywhere else the case is written in full:
var heading = Direction::North;
let back = heading == Direction::North; // true
error: '.North' must be written in full, as in 'Direction::North'
A case on its own, North, is an unknown name.
Conversions
as converts in both directions between an enum and any integer type:
let code = Status::NotFound as uint16; // 404
let received: uint16 = 404;
let status = received as Status; // Status::NotFound
No other conversion applies: an enum does not convert to a float, and a case is not its number, so Status::Ok == 200 is operator '==' cannot compare left operand 'Status' with right operand 'int'.
as does not check. Converting an integer that is no case's value gives an enum value that is none of the cases. Nothing goes wrong until a match meets it: a match without an else arm then stops the program with Panic: no match arm matched value of 'Status'. Check a number from outside the program before converting it.
Comparison
Enum values compare with ==, !=, <, <=, > and >=, which order them by their values:
let failed = status >= Status::NotFound; // true for 404 and 500
Matching
A match on an enum must cover every case, or end with an else arm. An arm names one case:
func TurnRight(direction: Direction) -> Direction {
return match direction {
.North => Direction::East,
.East => Direction::South,
.South => Direction::West,
.West => Direction::North
};
}
error: match on 'Direction' is not exhaustive; missing Direction::West
Leaving else out when every case matters means adding a case later makes every incomplete match stop compiling, at the place that needs the new arm. See match.
Methods
An enum can be extended with methods like any other type. A receiver taken by value, self: Direction, is natural for something as small as a number:
extend Direction {
func Opposite(self: Direction) -> Direction {
return (((self as int) + 2) % 4) as Direction;
}
}
An enum value cannot be printed with {} directly; give it a method that returns its name.
Rules and errors
| Mistake | Error |
|---|---|
A case with a payload, Circle(float64) | enum 'Shape' cannot declare payloads, with help: use 'variant' for cases that carry data |
Type parameters, enum Gen<T> | enum 'Gen' cannot declare type parameters |
| A non-integer backing type | enum 'Wide' base type must be an integer type |
| A case declared twice | duplicate enumerator 'A' in enum 'Twice' |
| The short form outside a pattern | '.North' must be written in full, as in 'Direction::North' |
A missing case in a match | match on 'Direction' is not exhaustive; missing Direction::West |
See also
- Variants — cases that carry data
- Casts —
asin general - Patterns — case patterns
- Learn: Enum, Enum value