Structs

A struct is a top-level list of typed fields, one per line. Calling the struct name with one argument per field, in order, creates a new value:

struct Point do
    int x
    int y
end

Point p = Point(1, 2)
p.x += 10
print(p.x, p.y) // 11 2

A field can have a default value, written after =, and values can be given by field name, in any order, after those given by position. Fields with a default can be left out:

struct Server do
    str host = "localhost"
    int port = 8080
    bool secure = false
end

Server local = Server()
Server api = Server("example.com", secure: true, port: 443)

A default is evaluated each time a struct is made, so each one gets its own array or map; it may use constants and call functions, but not variables. The values given are evaluated in the order written, then the defaults.

Struct values are references. Assignment and function calls share the same record rather than copying it, so a function can update a field of its argument. Fields can have any type, including the struct itself, which makes recursive data such as lists and trees possible:

struct Node do
    int value
    Node next
end

Node list = Node(1, Node(2, null))
print(list.next.value) // 2

Field access composes with calls and indexes, as in nodes[0].next.value. str, bytes, arrays, maps, and structs are reference types: they accept null and can be compared with it. Two structs compare by identity with == and !=. Reading or writing a field through null terminates the program with a runtime error. Structs cannot be printed or used in arithmetic.

Enums and structs may be used before their declaration. Their names cannot be reused for variables, functions, or other types.

Generic structs

A struct can take type parameters in brackets after its name, which its fields use like types:

struct Pair[A, B] do
    A first
    B second
end

struct Stack[T] do
    T[] items = []
end

Pair[str, int] entry = Pair("apples", 3)
Stack[f64] readings = Stack()
push(readings.items, 2.5)
Pair[int, int] origin = Pair[int, int](0, 0)

Pair[str, int] and Pair[int, str] are different types, and the type arguments must always be written in a type. When a struct is made, its type arguments are those written after its name, as in Pair[int, int](0, 0), or else those the place it goes expects, such as the declared type of a variable, or else they come from the values given, in order; when none of these tells, as for Stack() on its own, the compiler asks for them. Fields read and written through a Pair[str, int] have the types str and int, and the defaults of a generic struct are checked for each set of types it is made with.

A generic struct can be used anywhere a type can: in arrays and maps, in the fields of other structs, in the values that enum members carry, and with the type parameters of generic functions and enums, as in func top[T](Stack[T] s) T. Its fields can hold the struct itself, as in Node[T] next. The compiler generates a C struct for each set of type arguments the program uses.