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
struct value >>field Write a field (pushes modified struct back for chaining)
struct value >>field! Write a field (discards struct, for standalone mutation)
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. The << indicates data flows left (out of the struct).

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

Type narrowing for ptr values

When a value is typed as ptr (e.g., in callback handlers), use as to tell the compiler which struct type it is:

fn handler(c:ptr -- ) {
    c as http::Ctx <<body -> body
}

If multiple structs share a field name and the compiler cannot determine the type, accessing <<field on an untyped ptr is a compile error. Use as StructType to disambiguate.

See Type Casting for details.


Field set (write)

Use >>fieldname to write a field value. The struct must be on the stack, followed by the value. The >> indicates data flows right (into the struct). The modified struct is pushed back onto the stack.

Syntax: struct value >>field — returns modified struct (for chaining)

// Chaining — struct stays on stack
Point { x = 0.0 y = 0.0 } 1.0 >>x 2.0 >>y -> p

// Rebind after write
p 5.0 >>x -> p

Use >>field! (with !) when you don't need the struct back — for standalone mutations:

Syntax: struct value >>field! — discards struct after write

p 99 >>x!           // mutate, don't push struct back
s sz 1 + >>size!    // common in imperative code

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

struct Point {
    x:f64
    y:f64
}

fn (p:Point) translate(dx:f64 dy:f64 -- result:Point) {
    p p <<x dx + >>x p <<y dy + >>y
}

fn main() {
    Point { x = 1.0 y = 2.0 } -> p
    p 3.0 4.0 translate -> p
    p <<x print nl  // 4
    p <<y print nl  // 6
}

Method resolution

Methods take precedence over global functions with the same name:

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 (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.


See also