LEVIATHAN v962456e · 962456eee1

Declarations

Enums

Declare a closed set of named integer-backed values, convert to and from their codes, and match on them exhaustively.

since 0.1.0-alpha.1linuxwindowswasm

Description

An enum declares a closed set of named members. Each member carries an int value, its code. An enum is a value type: it is copied, has no identity, and cannot be inherited from.

enum Method { GET, HEAD, POST }                                // codes 0, 1, 2
enum Status : int { OK = 200, NotFound = 404, Teapot = 418 }   // explicit codes
enum Gap : int { A, B = 10, C }                                // A = 0, B = 10, C = 11

Members are reached with :: on the enum's name. code() returns the member's integer, and toString() returns its name. Method::fromCode(n) turns a code back into a member, and yields None when no member has that code, so its type is Method?. A variable declared without an initializer holds the first-declared member.

Members, codes, names and fromCode

enum Method { GET, HEAD, POST }
enum Status : int { OK = 200, NotFound = 404, Teapot = 418 }
enum Gap : int { A, B = 10, C }
Method m = Method::GET;
console.writeln(m.code());
console.writeln(m.toString());
console.writeln(Method::POST.code());
console.writeln(Gap::C.code());
Method d;
console.writeln(d.toString());
Method? found = Method::fromCode(1);
if (found != None) {
    console.writeln(found.toString());
}
console.writeln(Status::fromCode(999) == None);
console.writeln(Method::GET == Method::GET);
console.writeln(Method::GET != Method::POST);
console.writeln("${Status::NotFound.toString()} is ${Status::NotFound.code()}");
0
GET
2
11
GET
HEAD
true
true
true
NotFound is 404

Rules

  • Without : int the codes count up from 0. With explicit codes, a member that has none takes the previous member's code plus one (Gap::C is 11).
  • int is the only carrier type. Two members with the same code are a compile error.
  • == and != compare enum values by code.
  • An enum value used as a Map key or a Set element compares by value.
  • Enum::fromCode(int) returns Enum?, None if no member has that code.
  • match over an enum is exhaustive: when every member has an arm, no else is needed; leaving a member out is a compile error that names the missing members. An else arm is still allowed and covers the members not listed.
  • A bare declaration Method d; is the first-declared member.

Examples

Matching on an enum:

Exhaustive match and else

enum Method { GET, HEAD, POST }
enum Status : int { OK = 200, NotFound = 404, Teapot = 418 }
string name(Method x) {
    return match (x) {
        Method::GET => "read";
        Method::HEAD => "peek";
        Method::POST => "write";
    };
}
string kind(Status s) {
    return match (s) {
        Status::OK => "ok";
        else => "other";
    };
}
console.writeln(name(Method::HEAD));
console.writeln(kind(Status::Teapot));
Map<Method, string> verbs;
verbs = verbs.with(Method::POST, "create");
console.writeln(verbs.at(Method::POST));
peek
other
create

An incomplete match and duplicate codes are compile errors:

Errors

enum Method { GET, HEAD, POST }
string name(Method x) {
    return match (x) {
        Method::GET => "read";
        Method::HEAD => "peek";
    };   // error: non-exhaustive match on enum 'Method': missing Method::POST
}
enum Bad : int { A = 1, B = 1 }   // error: duplicate enum carrier value 1
not run — a compile error on purpose

Notes

Not implemented: carriers other than int (an enum S : string is a compile error), and ordering comparisons such as < between enum members. Compare the results of code() instead.

See also

  • Value structs — Declare value types that are copied on every assignment, pass and return, with derived equality and explicit mutating methods.