Enums and switch
An enum is a type whose value is one of a list of cases. In Cromo a case can carry data, so an enum can say not only which case a value is, but also what comes with it. Together with switch, which the compiler checks against every case, enums are the safest way to represent alternatives.
#Cases without data
enum Direction {
North
East
South
West
}
Direction turnRight(Direction d) {
switch (d) {
case North {
ret Direction.East
}
case East {
ret Direction.South
}
case South {
ret Direction.West
}
case West {
ret Direction.North
}
}
}
i32 main() {
Direction d = Direction.North
d = turnRight(turnRight(d))
println("{:?} {}", d, d == Direction.South)
ret 0
}South trueA value is written with the name of the enum, Direction.North; inside a switch on the enum, the case is enough. The default value of an enum is its first case.
The cases of an enum without data can have a value, which i32(...) reads and Color(n) turns back into a case:
enum Color {
Red = 1
Green = 2
Blue = 4
}
i32 main() {
Color c = Color.Green
println("{} {:?}", i32(c), Color(4))
ret 0
}2 BlueColor(3) would be a panic, since no case has the value 3.
#Cases with data
A case can carry values, written as the parameters of a function:
enum Shape {
Circle(f64 radius)
Rect(f64 width, f64 height)
Empty
}
f64 area(Shape shape) {
switch (shape) {
case Circle(f64 r) {
ret 3.14159 * r * r
}
case Rect(f64 w, f64 h) {
ret w * h
}
case Empty {
ret 0.0
}
}
}
i32 main() {
list<Shape> shapes = [Shape.Circle(1.0), Shape.Rect(2.0, 3.0), Shape.Empty]
for (Shape s in shapes) {
println("{:>12} {:.2f}", format("{:?}", s), area(s))
}
ret 0
} Circle(1.0) 3.14
Rect(2.0, 3.0) 6.00
Empty 0.00In case Circle(f64 r) the data of the case get a name, read-only and without a copy. A case can ignore its data by leaving out the parentheses.
#switch covers every case
On an enum, switch must handle every case, or have a default. Forgetting one is a compile error, not a bug found later:
enum Shape {
Circle(f64 radius)
Rect(f64 width, f64 height)
}
f64 area(Shape s) {
switch (s) {
case Circle(f64 r) {
ret 3.14159 * r * r
}
}
}
i32 main() {
ret 0
}error: 'switch' does not cover Rect of Shape: add the cases, or 'default'When a case is added to an enum, the compiler lists every switch that must learn about it.
A switch has no fall-through: each case runs its own block and the switch ends. Several cases without data can share a block: case North, South { ... }. A break inside a case is an error, because in C it would leave the switch, while in Cromo it would leave the loop around it.
#Modifying the data of a case
With ref, a case gives its data for modification:
enum Shape {
Circle(f64 radius)
Rect(f64 width, f64 height)
}
void grow(ref Shape s) {
switch (s) {
case Circle(ref f64 r) {
r *= 2.0
}
case Rect(ref f64 w, ref f64 h) {
w *= 2.0
h *= 2.0
}
}
}
i32 main() {
Shape s = Shape.Circle(1.5)
grow(ref s)
println("{:?}", s)
ret 0
}Circle(3.0)#Methods, and generic enums
An enum can have methods, as a struct. Inside a method, switch without a value works on the enum itself:
enum Token {
Number(i64 value)
Word(string text)
End
string describe() const {
switch {
case Number(i64 n) {
ret format("the number {}", n)
}
case Word(string w) {
ret format("the word '{}'", w)
}
case End {
ret "the end"
}
}
}
}
i32 main() {
list<Token> tokens = [Token.Number(42), Token.Word("hello"), Token.End]
for (Token t in tokens) {
println("{}", t.describe())
}
ret 0
}the number 42
the word 'hello'
the endAn enum can also have type parameters, as the generic structs of chapter 9. This one holds either a result or the reason of a failure:
enum Outcome<T> {
Done(T value)
Failed(string reason)
}
Outcome<i64> half(i64 n) {
if (n % 2 != 0) {
ret Outcome<i64>.Failed(format("{} is odd", n))
}
ret Outcome<i64>.Done(n / 2)
}
i32 main() {
list<i64> inputs = [10, 7]
for (i64 n in inputs) {
switch (half(n)) {
case Done(i64 v) {
println("half of {} is {}", n, v)
}
case Failed(string why) {
println("cannot: {}", why)
}
}
}
ret 0
}half of 10 is 5
cannot: 7 is odd#Summary
- An enum lists cases; a case can carry data, written as parameters.
switchon an enum must cover every case or havedefault, and the compiler checks it.case Circle(f64 r)names the data of a case; withrefthey can be modified.- Enums have methods and type parameters, as structs; the default value is the first case.