Structs
Structs are user-defined composite types with named, typed fields. They are reference types managed by the garbage collector.
Definition
Struct definitions are top-level declarations — they cannot be nested inside functions.
Creating Instances
Use the struct name followed by { field: value } syntax:
Fields can be provided in any order:
Field Access
Use dot notation to read fields:
Field Assignment
Use dot notation to modify fields:
Structs are reference types — assignment modifies the original, not a copy.
Default Values
Fields can have default values for number, i32, u8, and bool types:
struct Color {
r: u8 = 0
g: u8 = 0
b: u8 = 255
}
var blue: Color = Color {} # all defaults: r=0, g=0, b=255
var red: Color = Color { r: 255, b: 0 } # g defaults to 0
Only primitive types support defaults. Fields of type string, array, or another struct cannot have default values — the compiler will report an error:
struct Bad {
name: string = "hello" # ERROR: default values only for number, i32, u8, bool
items: array<number> # no default possible — must be provided
origin: Point # no default possible — must be provided
}
Fields without defaults must be provided:
struct Point {
x: number
y: number
}
var p: Point = Point { x: 10.0 } # ERROR: missing field 'y' (no default)
Nested Structs
A struct field can be another struct type:
struct Point {
x: number
y: number
}
struct Rect {
origin: Point
w: number
h: number
}
var r: Rect = Rect {
origin: Point { x: 5.0, y: 10.0 },
w: 100.0,
h: 50.0
}
Chained dot access works for nested structs:
Structs and Functions
Structs can be passed as function parameters and returned from functions:
struct Point {
x: number
y: number
}
fn make_point(x: number, y: number):Point {
return Point { x: x, y: y }
}
fn distance(p: Point):number {
return p.x + p.y
}
fn main():void {
var p: Point = make_point(3.0, 7.0)
var d: number = distance(p)
assert(d == 10.0)
}
Type Checking
The compiler enforces field types at compile time:
struct Point {
x: number
y: number
}
var p: Point = Point { x: "hello", y: 0.0 } # ERROR: expected number, got string
Numeric auto-coercion applies to struct fields (same rules as variables):
struct Sensor {
id: i32
value: number
}
var s: Sensor = Sensor { id: 42, value: 3.14 } # 42 auto-converts to i32
Reference Semantics
Structs are GC-managed reference types. Assigning a struct to another variable creates a shared reference, not a copy:
var a: Point = Point { x: 1.0, y: 2.0 }
var b: Point = a
b.x = 99.0
assert(a.x == 99.0) # both point to the same struct
Equality
Structs use reference equality — == compares whether two variables point to the same instance, not whether their fields have the same values:
var a: Point = Point { x: 1.0, y: 2.0 }
var b: Point = a
assert(a == b) # true — same instance
var c: Point = Point { x: 1.0, y: 2.0 }
assert(a != c) # true — different instances, even with identical fields
There is no built-in deep equality. To compare field values, compare each field explicitly.
Limits
| Limit | Value |
|---|---|
| Max struct definitions | 32 |
| Max fields per struct | 16 |
Exceeding either limit produces a compile error. The program will not build.
Bytecode
Struct operations use three opcodes:
| Opcode | Hex | Operand | Description |
|---|---|---|---|
| STRUCT_NEW | 0xA0 | u8 (field count) | Pop N field values, create struct, push it |
| STRUCT_GET | 0xA1 | u8 (field index) | Pop struct, push field value |
| STRUCT_SET | 0xA2 | u8 (field index) | Pop value + struct, set field, push struct |