Structs
Structs group related data together into custom types.
Defining structs
struct Point {
x:f64
y:f64
}
struct Person {
name:str
age:i64
}
Creating instances
Use the struct name with field initializers:
struct Point {
x:f64
y:f64
}
fn main() {
Point {
x = 10.0
y = 20.0
} -> p
p @x print nl // 10.0
p @y print nl // 20.0
}
Reading fields
Use @fieldname to read:
struct Rectangle {
width:f64
height:f64
}
fn area(rect:Rectangle -- a:f64) {
-> rect // bind parameter
rect @width rect @height *
}
fn main() {
Rectangle {
width = 5.0
height = 3.0
} -> rect
rect area print nl // 15.0
}
Writing fields
Use .fieldname to write:
struct Counter {
value:i64
}
fn main() {
Counter {
value = 0
} -> c
c @value print nl // 0
10 c .value
c @value print nl // 10
c @value inc c .value
c @value print nl // 11
}
Nested structs
Structs can contain other structs:
struct Point {
x:f64
y:f64
}
struct Line {
start:Point
end:Point
}
fn main() {
Point {
x = 0.0
y = 0.0
} -> p1
Point {
x = 10.0
y = 10.0
} -> p2
Line {
start = p1
end = p2
} -> line
line @start @x print nl // 0.0
line @end @x print nl // 10.0
}
Structs with arrays
struct Point {
x:f64
y:f64
}
struct Polygon {
points:ptr
count:i64
}
fn main() {
3 make<ptr> -> pts
Point {
x = 0.0
y = 0.0
} -> p0
Point {
x = 1.0
y = 0.0
} -> p1
Point {
x = 0.5
y = 1.0
} -> p2
pts 0 p0 set
pts 1 p1 set
pts 2 p2 set
Polygon {
points = pts
count = 3
} -> triangle
triangle @count print nl // 3
}
Struct methods
Methods are functions that operate on a specific struct type. Define them using receiver syntax:
use math
struct Point {
x:f64
y:f64
}
// Method with receiver syntax: fn (receiver:Type) name(params -- returns)
fn (p:Point) magnitude( -- m:f64) {
p @x dup * p @y dup * + math::sqrt
}
fn (p:Point) move(dx:f64 dy:f64 -- ) {
-> dy -> dx // bind parameters
p @x dx + p .x
p @y dy + p .y
}
fn main() {
Point { x = 3.0 y = 4.0 } -> pt
// Call method on struct - receiver is popped from stack
pt magnitude print nl // 5.0
// Methods can also work on inline struct construction
Point { x = 5.0 y = 12.0 } magnitude print nl // 13.0
// Method with parameters
pt 1.0 1.0 move
pt @x print nl // 4.0
}
Method resolution
When a struct is on the stack and you use an identifier, methods take precedence over global functions:
struct Counter {
value:i64
}
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 (calls method)
bump print nl // 999 (calls global function)
}
Traditional function style
You can also define functions that take structs as parameters:
use math
struct Point {
x:f64
y:f64
}
fn point_distance(p1:Point p2:Point -- d:f64) {
-> p2 -> p1 // bind parameters
p2 @x p1 @x - dup *
p2 @y p1 @y - dup *
+ math::sqrt
}
fn main() {
Point { x = 0.0 y = 0.0 } -> a
Point { x = 3.0 y = 4.0 } -> b
a b point_distance print nl // 5.0
}
Common patterns
Builder pattern
struct Config {
debug:i64
verbose:i64
max_retries:i64
}
fn config_new( -- cfg:Config) {
Config {
debug = 0
verbose = 0
max_retries = 3
}
}
fn config_set_debug(cfg:Config value:i64 -- cfg:Config) {
-> value -> cfg
value cfg .debug
cfg
}
fn main() {
config_new 1 config_set_debug -> cfg
cfg @debug print nl // 1
}
Linked list node
struct Node {
value:i64
next:ptr
}
fn node_new(value:i64 -- node:ptr) {
-> value // bind parameter
Node {
value = value
next = 0
}
}
fn main() {
10 node_new -> first
20 node_new -> second
30 node_new -> third
second first .next
third second .next
// Traverse
first -> current
current 0 != while {
current @value print nl
current @next -> current
current 0 !=
}
// Output: 10 20 30
}
Stack data structure
struct Stack {
data:ptr
top:i64
capacity:i64
}
fn stack_new(capacity:i64 -- s:Stack) {
-> capacity // bind parameter
capacity make<i64> -> data
Stack {
data = data
top = 0
capacity = capacity
}
}
fn stack_push(s:Stack value:i64 -- ) {
-> value -> s
s @data s @top value set
s @top 1 + s .top
}
fn stack_pop(s:Stack -- value:i64) {
-> s // bind parameter
s @top 1 - s .top
s @data s @top nth
}
fn main() {
10 stack_new -> s
s 1 stack_push
s 2 stack_push
s 3 stack_push
s stack_pop print nl // 3
s stack_pop print nl // 2
s stack_pop print nl // 1
}
Struct equality
Compare structs field by field:
struct Point {
x:f64
y:f64
}
fn points_equal(a:Point b:Point -- equal:i64) {
-> b -> a
a @x b @x == a @y b @y == and
}
fn main() {
Point {
x = 1.0
y = 2.0
} -> p1
Point {
x = 1.0
y = 2.0
} -> p2
Point {
x = 3.0
y = 4.0
} -> p3
p1 p2 points_equal print nl // 1 (true)
p1 p3 points_equal print nl // 0 (false)
}
What's next?
Learn about Constants to define fixed values.