Tagged Unions

Declaration

tape
tagged Shape {
    Circle { radius: f64 }
    Rect { width: f64, height: f64 }
    Point;
}

A tagged union stores exactly one variant at a time, with a hidden discriminant tag. Variants with no fields use ; instead of {}.

Construction

tape
let s = Shape.Circle { radius: 5.0 };
let r = Shape.Rect { width: 3.0, height: 4.0 };

Pattern matching

tape
fn area(s: Shape) -> f64 {
    match (s) {
        Shape.Circle { radius } => { return 3.14159 * radius * radius; }
        Shape.Rect { width; height } => { return width * height; }
        Shape.Point => { return 0.0; }
    }
}

Match arms use Type.Variant { field_bindings } syntax. Fields are bound by name. Use .. to ignore remaining fields, or : alias to rename a binding.

Type narrowing with is

Plannedis narrowing for tagged union variants is not yet implemented. Currently is only works with component constraint types.
tape
if (s is Shape.Circle) {
    io.print_f64(s.radius);
}

Untagged unions (t0/t1 only)

tape
union Register {
    i: i64;
    f: f64;
    bytes: [8]u8;
}

Like C’s union — all fields occupy the same memory, no discriminant tag. You’re responsible for knowing which interpretation is valid. Only available in t0/t1 profiles; no managed types (strings, slices, GC pointers) allowed inside. Use tagged unions for the safe “one of several types” case.

Last modified: