Skip to content

Structs

struct declares a nominal type:

struct Point {
Int x;
Int y;
}
struct Offset {
Int x, y;
}

Int x, y; is shorthand for two fields of the same type.

Struct layout options are written after struct:

struct [packed, align: 8] Packet {
UInt8 tag;
UInt32 value;
}

packed places fields without natural field padding. align: N sets the aggregate alignment to at least N, and N must be a power of two. Option order does not matter, so struct [align: 8, packed] is equivalent to struct [packed, align: 8]. Repeating the same option is an error.

Structs without custom init can use the compiler-provided default constructor. The default constructor uses field names as required argument labels:

Point point = Point(x: 0, y: 0);
Point^ heap_point = new Point(x: 100, y: 200);

Positional arguments are not accepted for the default constructor:

Point point = Point(0, 0); // error

Fields can have default values. A field with a default value can be omitted from the default constructor call:

struct Window {
Int width;
Int height = 600;
Bool visible = true;
}
Window window = Window(width: 800);

When an expected type is available, the type name can be omitted with .(...):

Window make_window() {
return .(width: 1024, visible: false);
}

Owning pointers can auto-dereference when the expected type is the pointee type. Field access can also pass through T^:

print(heap_point.x);

A field is writable only when both the access path and the field declaration are writable. An immutable binding, a shared T&, or another access path without write capability freezes reachable fields:

struct User {
Int id;
Int age!;
UInt8[]&? nick_name;
}
User user! = User(id: 123, age: 18)
user.age = user.age + 1;
// user.id = 200; // error
User& readonly = user$.ref();
// readonly.age = 20; // error: readonly outer borrow

init is a constructor entry in a struct body:

struct Point {
Int x;
Int y;
init(self, Int x, Int y) {
self.x = x;
self.y = y;
return ();
}
init(self, Int value) {
self.x = value;
self.y = value;
return ();
}
}
Point p1 = Point(1, 2);
Point p2 = Point(3);
Point^ p3 = new Point(4, 5);

Use init(self, ...) for ordinary custom constructors. self names the initializing target and is not supplied at the call site. Remaining parameters follow normal function argument rules, including named arguments and defaults. Once a struct declares any custom init, the compiler-provided field constructor is not used.

deinit is declared without a return type:

struct Buffer {
UInt8* data;
deinit(self) {
unsafe {
self.data$.dealloc();
}
return ();
}
}

Types define an instance method by putting a receiver first in the parameter list. Functions without a receiver are associated functions:

struct User {
Int id;
Int zero() {
return 0;
}
Int value(self) {
return self.id;
}
}
Int a = User.zero();
User u = User(id: 42)
Int b = u.value();

The receiver shorthand self is readonly Self&. A method that writes mutable fields declares Self&! self explicitly; borrow checking creates a unique mutable reborrow at the call site.

Local variable declarations bind one name at a time:

Int a = 1;
Int b = 2;
Int c = 3;