Structs
Custom data types that group related fields together.
Overview
| Syntax | Description |
|---|---|
struct Name { fields } |
Define a struct |
pub struct Name { fields } |
Define a public struct (visible to other modules) |
struct Name<T> { fields } |
Define a generic struct |
Name { field = value } |
Create an instance |
@field |
Read a field |
value struct.field |
Write a field |
fn (s:Name) method(...) |
Define a method |
Defining structs
struct Point {
x:f64
y:f64
}
Field types can be any valid type: i64, f64, str, ptr, another struct, or a pointer to a struct (*StructName).
Default values
Fields can have default values:
struct Config {
debug:i64 = 0
verbose:i64 = 0
max_retries:i64 = 3
}
fn main() {
Config {} -> cfg // all defaults
cfg @max_retries print nl // 3
}
Public structs
Use pub to export a struct from a module:
pub struct Vec2 {
x:f64
y:f64
}
Creating instances
Push field values with field = expr inside braces:
Point { x = 3.0 y = 4.0 } -> p
All fields without defaults must be provided. Order does not matter.
Field access (read)
Use @fieldname to read a field value. The struct is consumed from the stack and the field value is pushed.
Syntax: struct @field → pushes field value
p @x print nl // read x from p
Chained access
For nested structs, chain @ operators:
struct Line {
start:Point
end:Point
}
line @start @x print nl // read x from start point of line
Field set (write)
Use .fieldname to write a field value. The value to set must be on the stack before the struct.
Syntax: value struct.field
5.0 p.x // set p.x to 5.0
10.0 p.y // set p.y to 10.0
Methods
Methods are functions with a receiver parameter. The receiver uses a special syntax before the function name.
Syntax: fn (receiver:StructType) name(params -- returns) { body }
struct Circle {
radius:f64
}
fn (c:Circle) area( -- a:f64) {
c @radius dup * 3.14159 *
}
fn main() {
Circle { radius = 5.0 } -> c
c area print nl // 78.5397
}
Method calls
When a struct is on the stack and you call a function name, the compiler checks for a matching method on that struct type first:
c area // calls (c:Circle) area
The struct is consumed from the stack as the receiver.
Methods with parameters
fn (p:Point) translate(dx:f64 dy:f64 -- ) {
-> dy -> dx
p @x dx + p .x
p @y dy + p .y
}
fn main() {
Point { x = 1.0 y = 2.0 } -> p
p 3.0 4.0 translate
p @x print nl // 4
p @y print nl // 6
}
Method resolution
Methods take precedence over global functions with the same name:
fn (c:Counter) bump( -- result:i64) {
c @value 1 +
}
fn bump( -- result:i64) {
999
}
fn main() {
Counter { value = 10 } -> c
c bump print nl // 11 (method)
bump print nl // 999 (global)
}
Generic structs
Structs can have type parameters:
struct Box<T> {
value:T
}
fn main() {
Box<i64> { value = 42 } -> b
b @value print nl // 42
}
Multiple type parameters:
struct Pair<A, B> {
first:A
second:B
}
Note: Generic structs currently work best with i64 fields. Using f64 or str type parameters may return raw bits or pointers.