Enums whose variants hold values

enum Shape { Circle(float), Rect(float, float), Point }; Shape.Circle(2.0) makes one, match names the values
(Circle(r), _ to ignore), exhaustive like plain enums, printing shows Rect(2, 3.5). Underneath it is a struct (tag +
every variant's values), so copying, freeing, methods and valgrind-cleanliness come with it. Also: stray note files removed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015f73FesGZGYPkmDvUcj6QX
This commit is contained in:
Nexoniarz 2026-10-26 00:18:53 +01:00
commit 3e45b62812
13 changed files with 75170 additions and 66737 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -428,4 +428,4 @@ test (22 checks).
- The driver uses `sh` utilities (`rm`, `mkdir`), so it needs a POSIX shell. - The driver uses `sh` utilities (`rm`, `mkdir`), so it needs a POSIX shell.
- Generics are monomorphised (code size grows with instantiations); there are no trait bounds, a wrong use is an error - Generics are monomorphised (code size grows with instantiations); there are no trait bounds, a wrong use is an error
inside the instantiated body. inside the instantiated body.
- No payload enums yet (variants carry no data).

View file

@ -924,6 +924,29 @@ tests/cross.sh windows # every test built for Windows and run with Wine
tests/cross.sh arm64 # every test built for ARM64 Linux and run with QEMU tests/cross.sh arm64 # every test built for ARM64 Linux and run with QEMU
``` ```
### Variants that hold values
A variant can carry values. Make one with `Shape.Circle(2.0)`, take them out with `match`, which names them:
```c
enum Shape {
Circle(float)
Rect(float, float)
Point
}
fn Shape.area() -> float {
return match self {
Circle(r) -> 3.14 * r * r
Rect(w, h) -> w * h
Point -> 0.0
}
}
```
`_` ignores a value. `match` must handle every variant (or end with `else`). Printing shows `Rect(2, 3.5)`. Such an enum is
copied and freed like a struct; it can't be compared with `==` (match on it). Variants of these enums can't have numbers.
--- ---
## 23. Enums and match ## 23. Enums and match
@ -1027,7 +1050,7 @@ write_file(path.join("out", "config.json"), json.pretty(doc))
Not there yet: Not there yet:
- Interfaces, and enums whose variants carry values (`Circle(radius)`). - Interfaces.
- Unicode-aware text: `.len`, `s[i]` and `upper()` work on bytes / ASCII. - Unicode-aware text: `.len`, `s[i]` and `upper()` work on bytes / ASCII.
- Threads. - Threads.
- Windows on ARM64, 32-bit systems, WebAssembly. - Windows on ARM64, 32-bit systems, WebAssembly.

View file

@ -421,4 +421,4 @@ serwer podpowiada dalej z ostatniej wersji, która się skompilowała. Test akce
- Sterownik używa narzędzi `sh` (`rm`, `mkdir`), więc wymaga powłoki POSIX. - Sterownik używa narzędzi `sh` (`rm`, `mkdir`), więc wymaga powłoki POSIX.
- Generyki są monomorfizowane (kod rośnie z liczbą instancji); nie ma ograniczeń na typy, błędne użycie to błąd wewnątrz - Generyki są monomorfizowane (kod rośnie z liczbą instancji); nie ma ograniczeń na typy, błędne użycie to błąd wewnątrz
wygenerowanego ciała. wygenerowanego ciała.
- Jeszcze nie ma enumów z danymi (warianty nie niosą wartości).

View file

@ -928,6 +928,29 @@ tests/cross.sh windows # każdy test zbudowany na Windowsa i uruchomiony w
tests/cross.sh arm64 # każdy test zbudowany na Linuksa ARM64 i uruchomiony w QEMU tests/cross.sh arm64 # każdy test zbudowany na Linuksa ARM64 i uruchomiony w QEMU
``` ```
### Warianty z wartościami
Wariant może nieść wartości. Tworzysz go przez `Shape.Circle(2.0)`, a wyciągasz `match`-em, który je nazywa:
```c
enum Shape {
Circle(float)
Rect(float, float)
Point
}
fn Shape.area() -> float {
return match self {
Circle(r) -> 3.14 * r * r
Rect(w, h) -> w * h
Point -> 0.0
}
}
```
`_` pomija wartość. `match` musi obsłużyć każdy wariant (albo kończyć się `else`). Wypisanie pokazuje `Rect(2, 3.5)`. Taki enum
jest kopiowany i zwalniany jak struktura; nie da się go porównać przez `==` (użyj `match`). Warianty takich enumów nie mają numerów.
--- ---
## 23. Enumy i match ## 23. Enumy i match
@ -1031,7 +1054,7 @@ write_file(path.join("out", "config.json"), json.pretty(dok))
Jeszcze nie ma: Jeszcze nie ma:
- Interfejsów i enumów, których warianty niosą wartości (`Kolo(promien)`). - Interfejsów.
- Tekstu świadomego Unicode: `.len`, `s[i]` i `upper()` działają na bajtach / ASCII. - Tekstu świadomego Unicode: `.len`, `s[i]` i `upper()` działają na bajtach / ASCII.
- Wątków. - Wątków.
- Windowsa na ARM64, systemów 32-bitowych, WebAssembly. - Windowsa na ARM64, systemów 32-bitowych, WebAssembly.

View file

@ -1 +0,0 @@
Kupić mleko

View file

@ -1 +0,0 @@
Buy milk

View file

@ -63,6 +63,22 @@ struct Cleanup {
int idx int idx
} }
// An enum whose variants hold values (see sumStruct in parser.fch): it is a struct `tag, V_0, ...`.
// firsts[k] is the number of the struct field holding variant k's first value.
struct SumDef {
str module
str name
[]str variants
[]int counts
[]int firsts
[]ast.Node nodes
}
struct SumPat {
int k
[]ast.Node binds
}
struct Scope { struct Scope {
[]Var vars []Var vars
[]ast.Node defers []ast.Node defers
@ -167,6 +183,7 @@ struct Gen {
[]ast.Type targs []ast.Type targs
str instNote // " (while building max[Pixel])", added to errors found inside a generic str instNote // " (while building max[Pixel])", added to errors found inside a generic
[]EnumDef enums []EnumDef enums
[]SumDef sums
[]str done []str done
[]str mm // names of methods that change self, for the mutation analysis []str mm // names of methods that change self, for the mutation analysis
int nvars // variables declared so far in this function int nvars // variables declared so far in this function
@ -483,6 +500,15 @@ fn findEnum(ptr[Gen] g, str module, str name) -> int {
return -1 return -1
} }
fn findSum(ptr[Gen] g, str module, str name) -> int {
for i in 0..g.sums.len {
if g.sums[i].module == module && g.sums[i].name == name {
return i
}
}
return -1
}
fn progOf(ptr[Gen] g, str module) -> int { fn progOf(ptr[Gen] g, str module) -> int {
for i in 0..g.progs.len { for i in 0..g.progs.len {
if g.progs[i].module == module { if g.progs[i].module == module {
@ -1172,6 +1198,43 @@ fn formatFn(ptr[Gen] g, ast.Type t) -> str {
label(g, next) label(g, next)
loopEnd(g, l) loopEnd(g, l)
bufText(g, "%buf", "}") bufText(g, "%buf", "}")
} else if t.kind == "struct" && findSum(g, t.module, t.name) >= 0 {
// Circle(2.5), Point
sm := g.sums[findSum(g, t.module, t.name)]
sd := g.structs[findStruct(g, t.module, t.name)]
st := llty(g, t)
tag := load(g, "i64", op(g, "getelementptr inbounds " + st + ", ptr %a, i32 0, i32 " + str(sd.lidx[0])))
def := newLabel(g, "sum.other")
done := newLabel(g, "sum.done")
[]str cases
[]str labels
for k in 0..sm.variants.len {
labels.push(newLabel(g, "sum.v"))
cases.push("i64 " + str(k) + ", label %" + labels[k])
}
emit(g, "switch i64 " + tag + ", label %" + def + " [ " + cases.join(" ") + " ]")
g.term = true
for k in 0..sm.variants.len {
label(g, labels[k])
bufText(g, "%buf", sm.variants[k])
if sm.counts[k] > 0 {
bufText(g, "%buf", "(")
for j in 0..sm.counts[k] {
if j > 0 {
bufText(g, "%buf", ", ")
}
fi := sm.firsts[k] + j
fp := op(g, "getelementptr inbounds " + st + ", ptr %a, i32 0, i32 " + str(sd.lidx[fi]))
emitFormat(g, "%buf", load(g, llty(g, sd.ftypes[fi]), fp), sd.ftypes[fi], true)
}
bufText(g, "%buf", ")")
}
br(g, done)
}
label(g, def)
bufText(g, "%buf", sm.name + "(?)")
br(g, done)
label(g, done)
} else { } else {
sd := g.structs[findStruct(g, t.module, t.name)] sd := g.structs[findStruct(g, t.module, t.name)]
st := llty(g, t) st := llty(g, t)
@ -1460,6 +1523,24 @@ fn declare(ptr[Gen] g) {
g.module = g.progs[pi].module g.module = g.progs[pi].module
for ei in 0..g.progs[pi].enums.len { for ei in 0..g.progs[pi].enums.len {
e := g.progs[pi].enums[ei] e := g.progs[pi].enums[ei]
if e.flag {
if findEnum(g, g.module, e.name) >= 0 || findSum(g, g.module, e.name) >= 0 {
fail(g, e, "'" + e.name + "' is defined twice")
}
sd := SumDef(module: g.module, name: e.name)
first := 1
for v in e.kids {
if sd.variants.contains(v.name) {
fail(g, v, "the enum " + e.name + " has the variant " + v.name + " twice")
}
sd.variants.push(v.name)
sd.counts.push(v.kids.len)
sd.firsts.push(first)
first += v.kids.len
}
g.sums.push(sd)
continue
}
if findEnum(g, g.module, e.name) >= 0 || findStruct(g, g.module, e.name) >= 0 { if findEnum(g, g.module, e.name) >= 0 || findStruct(g, g.module, e.name) >= 0 {
fail(g, e, "'" + e.name + "' is defined twice") fail(g, e, "'" + e.name + "' is defined twice")
} }
@ -2965,6 +3046,14 @@ fn matchGen(ptr[Gen] g, ast.Node e, bool asValue) -> Val {
} }
mark := g.pending.len mark := g.pending.len
hold(g, subj) // a temporary subject lives through the arms hold(g, subj) // a temporary subject lives through the arms
si := -1
tagv := ""
if st.kind == "struct" {
si = findSum(g, st.module, st.name)
if si >= 0 {
tagv = op(g, "extractvalue " + llty(g, st) + " " + subj.v + ", " + str(g.structs[findStruct(g, st.module, st.name)].lidx[0]))
}
}
end := newLabel(g, "match.end") end := newLabel(g, "match.end")
hasElse := false hasElse := false
reached := false // does any arm go on to the end? reached := false // does any arm go on to the end?
@ -2980,28 +3069,61 @@ fn matchGen(ptr[Gen] g, ast.Node e, bool asValue) -> Val {
} }
body := newLabel(g, "match.arm") body := newLabel(g, "match.arm")
next := newLabel(g, "match.next") next := newLabel(g, "match.next")
[]ast.Node binds
bindK := -1
if arm.flag { if arm.flag {
hasElse = true hasElse = true
br(g, body) br(g, body)
} else { } else {
pats := arm.kids[0].kids pats := arm.kids[0].kids
for pi in 0..pats.len { for pi in 0..pats.len {
pv := patternValue(g, pats[pi], st) hitv := ""
if covered.contains(pv.v) && st.kind == "enum" { if si >= 0 {
fail(g, pats[pi], "this variant is already handled by an earlier arm") sp := sumPattern(g, pats[pi], si)
if covered.contains(str(sp.k)) {
fail(g, pats[pi], "this variant is already handled by an earlier arm")
}
covered.push(str(sp.k))
if sp.binds.len > 0 {
if pats.len > 1 {
fail(g, pats[pi], "an arm with several variants can't name their values")
}
binds = sp.binds
bindK = sp.k
}
hitv = op(g, "icmp eq i64 " + tagv + ", " + str(sp.k))
} else {
pv := patternValue(g, pats[pi], st)
if covered.contains(pv.v) && st.kind == "enum" {
fail(g, pats[pi], "this variant is already handled by an earlier arm")
}
covered.push(pv.v)
hitv = compare(g, "==", subj, pv, pats[pi]).v
} }
covered.push(pv.v)
hit := compare(g, "==", subj, pv, pats[pi])
if pi == pats.len - 1 { if pi == pats.len - 1 {
condBr(g, hit.v, body, next) condBr(g, hitv, body, next)
} else { } else {
more := newLabel(g, "match.or") more := newLabel(g, "match.or")
condBr(g, hit.v, body, more) condBr(g, hitv, body, more)
label(g, more) label(g, more)
} }
} }
} }
label(g, body) label(g, body)
scoped := binds.len > 0
if scoped {
pushScope(g)
sx := g.structs[findStruct(g, st.module, st.name)]
for j in 0..binds.len {
if binds[j].name == "_" {
continue
}
fi := g.sums[si].firsts[bindK] + j
ft := sx.ftypes[fi]
fv := op(g, "extractvalue " + llty(g, st) + " " + subj.v + ", " + str(sx.lidx[fi]))
addVar(g, binds[j], binds[j].name, ft, copyValue(g, fv, ft), true, true)
}
}
node := arm.kids[1] node := arm.kids[1]
if asValue { if asValue {
v := expr(g, node) v := expr(g, node)
@ -3013,6 +3135,9 @@ fn matchGen(ptr[Gen] g, ast.Node e, bool asValue) -> Val {
} }
v = coerce(g, v, resultTy, node, "this arm (the arms of a match have to give the same type)") v = coerce(g, v, resultTy, node, "this arm (the arms of a match have to give the same type)")
out := own(g, v) out := own(g, v)
if scoped {
popScope(g)
}
from := newLabel(g, "match.from") from := newLabel(g, "match.from")
br(g, from) br(g, from)
label(g, from) label(g, from)
@ -3029,6 +3154,9 @@ fn matchGen(ptr[Gen] g, ast.Node e, bool asValue) -> Val {
if !g.term { if !g.term {
reached = true reached = true
} }
if scoped {
popScope(g)
}
br(g, end) br(g, end)
} }
if !arm.flag { if !arm.flag {
@ -3036,7 +3164,17 @@ fn matchGen(ptr[Gen] g, ast.Node e, bool asValue) -> Val {
} }
} }
if !hasElse { if !hasElse {
if st.kind == "enum" { if si >= 0 {
[]str missing
for k in 0..g.sums[si].variants.len {
if !covered.contains(str(k)) {
missing.push(g.sums[si].variants[k])
}
}
if missing.len > 0 {
fail(g, e, "this match doesn't handle " + missing.join(", ") + " (add them, or an 'else' arm)")
}
} else if st.kind == "enum" {
ed := g.enums[findEnum(g, st.module, st.name)] ed := g.enums[findEnum(g, st.module, st.name)]
[]str missing []str missing
for k in 0..ed.variants.len { for k in 0..ed.variants.len {
@ -3651,6 +3789,83 @@ fn enumNamed(ptr[Gen] g, ast.Node n) -> int {
return -1 return -1
} }
// Which payload enum does `n` name? Shape, or shapes.Shape. -1 if neither.
fn sumNamed(ptr[Gen] g, ast.Node n) -> int {
if n.kind == "var" && lookup(g, n.name) < 0 {
return findSum(g, g.module, n.name)
}
if n.kind == "member" && n.kids[0].kind == "var" && lookup(g, n.kids[0].name) < 0 && imports(g, n.kids[0].name) {
return findSum(g, n.kids[0].name, n.name)
}
return -1
}
fn sumVariant(ptr[Gen] g, int si, str name, ast.Node at) -> int {
sd := g.sums[si]
k := sd.variants.find(name)
if k < 0 {
fail(g, at, "the enum " + sd.name + " has no variant '" + name + "' (it has: " + sd.variants.join(", ") + ")")
}
return k
}
// Shape.Circle(2.0), or Shape.Point
fn sumMake(ptr[Gen] g, int si, str vname, []ast.Node args, ast.Node at) -> Val {
sd := g.sums[si]
k := sumVariant(g, si, vname, at)
if args.len != sd.counts[k] {
if sd.counts[k] == 0 {
fail(g, at, vname + " holds nothing: write " + sd.name + "." + vname)
}
fail(g, at, vname + " holds " + str(sd.counts[k]) + " value(s), but got " + str(args.len) + ": " + sd.name + "." + vname + "(...)")
}
[]ast.Node all
[]str names
all.push(ast.Node(kind: "int", text: str(k), line: at.line, col: at.col, file: at.file))
names.push("tag")
for j in 0..args.len {
all.push(args[j])
names.push(vname + "_" + str(j))
}
noteRef(g, at, at.col, vname.len, "```finch\n" + sd.name + "." + vname + "\n```", "", 0, 0)
return construct(g, findStruct(g, sd.module, sd.name), all, names, at)
}
// the variant a match pattern names, and the names it binds: Circle(r) Shape.Rect(w, h) Point
fn sumPattern(ptr[Gen] g, ast.Node pat, int si) -> SumPat {
sd := g.sums[si]
name := ""
[]ast.Node binds
if pat.kind == "var" && lookup(g, pat.name) < 0 {
name = pat.name
} else if pat.kind == "call" {
name = pat.name
binds = pat.kids
} else if pat.kind == "member" && sumNamed(g, pat.kids[0]) == si {
name = pat.name
} else if pat.kind == "method" && sumNamed(g, pat.kids[0]) == si {
name = pat.name
for q in 1..pat.kids.len {
binds.push(pat.kids[q])
}
} else {
fail(g, pat, "this pattern is not a variant of " + sd.name + " (it has: " + sd.variants.join(", ") + ")")
}
k := sumVariant(g, si, name, pat)
if binds.len != sd.counts[k] {
if sd.counts[k] == 0 {
fail(g, pat, name + " holds nothing: write just " + name)
}
fail(g, pat, name + " holds " + str(sd.counts[k]) + " value(s): name them, like " + name + "(x), or use _ to ignore one")
}
for b in binds {
if b.kind != "var" {
fail(g, b, "write a name to hold the value (or _ to ignore it)")
}
}
return SumPat(k: k, binds: binds)
}
// Color.Red // Color.Red
fn enumValue(ptr[Gen] g, ast.Node m, int ei) -> Val { fn enumValue(ptr[Gen] g, ast.Node m, int ei) -> Val {
ed := g.enums[ei] ed := g.enums[ei]
@ -3663,6 +3878,18 @@ fn enumValue(ptr[Gen] g, ast.Node m, int ei) -> Val {
} }
fn member(ptr[Gen] g, ast.Node m, bool write) -> Ref { fn member(ptr[Gen] g, ast.Node m, bool write) -> Ref {
sm := sumNamed(g, m.kids[0])
if sm >= 0 {
if write {
fail(g, m, "a variant of an enum can't be changed")
}
k := sumVariant(g, sm, m.name, m)
if g.sums[sm].counts[k] > 0 {
fail(g, m, m.name + " holds " + str(g.sums[sm].counts[k]) + " value(s): " + g.sums[sm].name + "." + m.name + "(...)")
}
[]ast.Node none
return Ref(isPlace: false, val: sumMake(g, sm, m.name, none, m))
}
ei := enumNamed(g, m.kids[0]) ei := enumNamed(g, m.kids[0])
if ei >= 0 { if ei >= 0 {
if write { if write {
@ -4999,6 +5226,14 @@ fn noteMethod(ptr[Gen] g, ast.Node m, []docs.Doc list) {
fn method(ptr[Gen] g, ast.Node m) -> Val { fn method(ptr[Gen] g, ast.Node m) -> Val {
objNode := m.kids[0] objNode := m.kids[0]
sm := sumNamed(g, objNode)
if sm >= 0 {
[]ast.Node vals
for q in 1..m.kids.len {
vals.push(m.kids[q])
}
return sumMake(g, sm, m.name, vals, m)
}
if objNode.kind == "var" && lookup(g, objNode.name) < 0 { if objNode.kind == "var" && lookup(g, objNode.name) < 0 {
mn := objNode.name mn := objNode.name
if !imports(g, mn) { if !imports(g, mn) {

View file

@ -249,7 +249,11 @@ fn parseProgram(ptr[Parser] p) -> ast.Program {
} else if at(p, "struct") { } else if at(p, "struct") {
prog.structs.push(parseStruct(p)) prog.structs.push(parseStruct(p))
} else if at(p, "enum") { } else if at(p, "enum") {
prog.enums.push(parseEnum(p)) e := parseEnum(p)
prog.enums.push(e)
if e.flag {
prog.structs.push(sumStruct(e)) // an enum with values inside is stored as a struct (see sumStruct)
}
} else { } else {
prog.fns.push(parseFn(p)) prog.fns.push(parseFn(p))
} }
@ -259,6 +263,7 @@ fn parseProgram(ptr[Parser] p) -> ast.Program {
// enum Color { Red, Green, Blue } enum Status { Ok = 200, NotFound = 404 } // enum Color { Red, Green, Blue } enum Status { Ok = 200, NotFound = 404 }
// The variants are numbered from 0 (or from the last number given) and are written Color.Red. // The variants are numbered from 0 (or from the last number given) and are written Color.Red.
// A variant can hold values: enum Shape { Circle(float), Rect(float, float), Point } (e.flag is then true).
fn parseEnum(ptr[Parser] p) -> ast.Node { fn parseEnum(ptr[Parser] p) -> ast.Node {
e := node(p, "enum") e := node(p, "enum")
expect(p, "enum", "'enum'") expect(p, "enum", "'enum'")
@ -270,7 +275,25 @@ fn parseEnum(ptr[Parser] p) -> ast.Node {
} }
v := node(p, "variant") v := node(p, "variant")
v.name = expect(p, "ident", "a variant name, like Red").text v.name = expect(p, "ident", "a variant name, like Red").text
if accept(p, "(") {
e.flag = true
while !at(p, ")") {
f := node(p, "field")
f.ty = parseType(p)
v.kids.push(f)
if !accept(p, ",") {
break
}
}
expect(p, ")", "')' after the values of the variant")
if v.kids.len == 0 {
fail(p, cur(p), "the variant " + v.name + " has empty () : write just " + v.name + " if it holds nothing")
}
}
if accept(p, "=") { if accept(p, "=") {
if e.flag {
fail(p, cur(p), "a variant can't have both values inside and a number")
}
sign := "" sign := ""
if accept(p, "-") { if accept(p, "-") {
sign = "-" sign = "-"
@ -286,9 +309,34 @@ fn parseEnum(ptr[Parser] p) -> ast.Node {
if e.kids.len == 0 { if e.kids.len == 0 {
fail(p, cur(p), "the enum " + e.name + " has no variants") fail(p, cur(p), "the enum " + e.name + " has no variants")
} }
if e.flag {
for v in e.kids {
if v.text.len > 0 {
fail(p, cur(p), "an enum whose variants hold values can't number them (" + v.name + " = " + v.text + ")")
}
}
}
return e return e
} }
// An enum whose variants hold values is a struct underneath: a `tag` (which variant it is) and, one after the other,
// the values of every variant (Rect's are Rect_0, Rect_1). Only the tag's variant has values; the rest stay
// empty. It's simple and it makes copying, dropping and methods the struct's own.
fn sumStruct(ast.Node e) -> ast.Node {
s := ast.Node(kind: "struct", name: e.name, line: e.line, col: e.col, file: e.file)
tag := ast.Node(kind: "field", name: "tag", line: e.line, col: e.col, file: e.file)
tag.ty = ast.simple("int")
s.kids.push(tag)
for v in e.kids {
for j in 0..v.kids.len {
f := ast.Node(kind: "field", name: v.name + "_" + str(j), line: v.line, col: v.col, file: v.file)
f.ty = v.kids[j].ty
s.kids.push(f)
}
}
return s
}
// match value { A, B -> ... C -> ... else -> ... } // match value { A, B -> ... C -> ... else -> ... }
// As a statement the arms are statements or { blocks }; as an expression they are expressions. // As a statement the arms are statements or { blocks }; as an expression they are expressions.
fn parseMatch(ptr[Parser] p, bool asExpr) -> ast.Node { fn parseMatch(ptr[Parser] p, bool asExpr) -> ast.Node {

View file

@ -0,0 +1,12 @@
// expect: this match doesn't handle Point
enum Shape {
Circle(float)
Point
}
fn main() {
s := Shape.Circle(1.0)
match s {
Circle(r) -> print(r)
}
}

View file

@ -0,0 +1,38 @@
enum Shape {
Circle(float)
Rect(float, float)
Named(str, []int)
Point
}
fn Shape.area() -> float {
return match self {
Circle(r) -> 3.0 * r * r
Rect(w, h) -> w * h
Named(_, sizes) -> float(sizes.len)
Point -> 0.0
}
}
fn describe(Shape s) -> str {
match s {
Shape.Circle(r) -> return "circle " + str(r)
Shape.Named(name, _) -> return "named " + name
else -> return "other"
}
}
fn main() {
shapes := [Shape.Circle(2.0), Shape.Rect(2.0, 3.5), Shape.Named("tri", [1, 2, 3]), Shape.Point]
for s in shapes {
print(s, s.area(), describe(s))
}
copy := shapes[2]
shapes.clear()
print(copy)
p := Shape.Point
match p {
Point -> print("a point")
else -> print("no")
}
}

View file

@ -0,0 +1,6 @@
Circle(2) 12 circle 2
Rect(2, 3.5) 7 other
Named("tri", [1, 2, 3]) 3 named tri
Point 0 other
Named("tri", [1, 2, 3])
a point