Enums and Matching
Declaring variants
Each variant names a shape, optionally carrying payloads in parentheses. Type parameters are permitted and are erased at runtime.
enum Shape { Circle(Float), Rect(Float, Float), Point }Construction mirrors the declaration: Shape.Circle(2.0), Color.Green, Result.Err("x"). Unit variants are bare idents; payload variants call like functions. Qualified construction always uses a dot: Result.Ok and Result.Err, never ::.
Two positions already name the enum, so only the variant has to follow the leading dot: a variable annotation, and a call argument whose parameter type the signature writes.
let r: Result<Int, String> = .Err("bad");
print(r.isErr());Argument inference reaches the same way through a method, a static fn, and an init parameter:
enum Color { Red, Green, Blue }
fn paint(c: Color): Int {
switch c {
case .Red: return 1;
case .Green: return 2;
case .Blue: return 3;
}
}
class Canvas {
let mode: Color;
init(c: Color) { this.mode = c; }
fn setMode(c: Color): Int { return paint(c); }
static fn withColor(c: Color): Int { return paint(c); }
}
let canvas = new Canvas(.Blue);
print(paint(.Green), canvas.setMode(.Red), Canvas.withColor(.Green));That prints 2 1 2. A name the enum does not declare is a compile-time error[E108]: unknown variant, and the message names both the variant and its enum. let bg: Color = .Nope; and paint(.Nope) both fail that way, with a pointer at the dot.
Library enums infer the same way. File modes in @std/fs take OpenMode and WriteMode, so call sites pass only the variant:
import { File } from "@std/fs";
let w = File.create("/tmp/rnx-dot.txt", .Overwrite);
w.writeText("hi");
w.close();
let r = File.open("/tmp/rnx-dot.txt", .Read);
print(r.readText().unwrapOr("?"));
r.close();That prints hi.
Matching exhaustively
switch over an enum value pairs each shape with behavior. Variant patterns lead with a dot and bind payloads to names. Covering every variant with unguarded cases needs no default: the checker proves exhaustiveness, and adding a variant later without updating the switch fails compilation naming the missing case.
enum Shape { Circle(Float), Rect(Float, Float), Point }
fn area(s: Shape): Float {
switch s {
case .Circle(r): return 3.14 * r * r;
case .Rect(w, h): return w * h;
case .Point: return 0.0;
}
}
print(area(Shape.Circle(2.0)));
print(area(Shape.Rect(3.0, 4.0)));Guards narrow a case further (case .Circle(r) if r > 0) and observe payload bindings; default catches everything the listed patterns miss. Payload arity is checked at construction: Circle(1.0, 2.0) against a one-payload variant fails immediately.
Matching beyond enums
The same statement matches literal ranges and dispatches on error values. Type tests use if value is Type, which branches on the runtime type — including the payload bound in a catch block, where a passing test narrows the binding so fields are accessible:
fn kind(code: Int): String {
switch code {
case 0..=99: return "info";
case 200..=299: return "ok";
default: return "other";
}
}
print(kind(42), kind(404));class IoError {
let message: String;
init(message: String) { this.message = message; }
}
fn Main(): Int {
let e = new IoError("disk gone");
if e is IoError {
print("io");
}
try {
throw new IoError("disk gone");
} catch (err) {
switch err {
case is IoError:
print("caught io");
pass;
default:
print("caught other");
pass;
}
}
return 0;
}Destructuring in for bindings
for (a, b) in pairs destructures each element: array elements by index, record and struct elements by field name. Anything else at runtime is a fatal error, and for (a, b) over a range is a compile error rather than a silent misread.
Summary
- Enums declare shapes with payloads; construction mirrors declaration with dot qualification.
switchwith leading-dot patterns, guards, auto-break, and provable exhaustiveness.is Typetests for mixed values; destructuringforbindings for pairs.