Struct
Overview
Structs are composite types that group named fields. Methods are defined via trait implementations.
Declaration
struct:Point = {
int32:x;
int32:y;
};
struct:Person = {
string:name;
int32:age;
};
Instantiation and Access
Struct literal syntax with named fields:
stack Point:p = Point{x: 10, y: 20};
println(`&{p.x}`); // 10
println(`&{p.y}`); // 20
stack Person:alice = Person{name: "Alice", age: 30};
Positional arguments (order must match declaration):
Point:p = Point{10i32, 20i32};
Constructor syntax (desugars to Type_create(args)):
Counter:c = raw instance<Counter>(10i32);
Fields can also be assigned after declaration:
Point:p;
p.x = 10i32;
p.y = 20i32;
Methods (via Trait Impls)
Methods are defined through trait implementations:
trait:HasArea = {
func:area: int32(Rect2D:self);
};
struct:Rect2D = {
int32:w;
int32:h;
};
impl:HasArea:for:Rect2D = {
func:area = int32(Rect2D:self) {
pass self.w * self.h;
};
};
// Call via UFCS (Uniform Function Call Syntax)
Rect2D:rect;
rect.w = 10i32;
rect.h = 5i32;
Result<int32>:a = rect.area();
Passing Structs
func:add_coords = int32(Point:p) {
pass p.x + p.y;
};
Point:origin = Point{ x: 0, y: 0 };
int32:sum = raw add_coords(origin);
Related
- enum.md — enumeration types
- memory_model/handle.md — Handle
for arena structs - control_flow/pick.md — struct field pattern matching in pick
Struct Update Syntax (v0.19.1)
Create a copy of a struct with selected fields overridden:
struct:Point2D = {
int32:x;
int32:y;
};
Point2D:origin = Point2D{ x: 0i32, y: 0i32 };
Point2D:shifted = Point2D{ ...origin, x: 5i32 };
// shifted.x = 5, shifted.y = 0 (copied from origin)
Rules:
- The base variable (...base) must be the same struct type as the target.
- All named field overrides are applied after copying the base.
- The base is unchanged (value copy, not mutation).
- The base must be a plain owned variable, not a $$m borrow alias.
- Unknown field names in overrides are a compile-time error.
Extern Functions Returning Structs (v0.19.1)
Prior to v0.19.1, returning a struct value from an extern call required storing to a temporary variable first. This restriction is lifted:
// v0.19.1+: direct pass of extern-returned struct value works correctly
extern func:get_point = Point();
func:example = int32() {
pass raw get_point().x; // direct field access on extern return
};
Struct Equality (v0.35.4)
Struct values can be compared with == when both operands are the same struct
type. Equality expands to per-field comparisons:
struct:Point = { int32:x; int32:y; };
Point:a = Point{ x: 3, y: 4 };
Point:b = Point{ x: 3, y: 4 };
Point:c = Point{ x: 0, y: 0 };
if (a == b) { /* true — all fields match */ };
if (a == c) { /* false */ };
Comparing two incompatible struct types is a compile-time type error.
Struct == does not imply deep equality of heap-allocated fields (pointers
compare by address, not by referent content).
See also docs/abi.md for the struct FFI ABI table (by-value vs by-pointer
thresholds, padding and alignment rules).
dyn Trait struct fields (v0.35.5)
A struct field may be declared as dyn TraitName, making that field a
fat pointer ({data*, vtable*}) that can hold any concrete type for
which the trait is implemented:
trait:Drawable = {
func:draw = int32(Drawable:self);
};
struct:Circle = { int32:radius; };
struct:Widget = { dyn Drawable:inner; };
impl:Drawable:for:Circle = {
func:draw = int32(Circle:self) { pass self.radius; };
};
func:main = int32() {
Circle:c = Circle{ radius: 42 };
Widget:w;
w.inner = c; // concrete Circle → dyn Drawable coercion
int32:r = w.inner.draw(); // dynamic dispatch through vtable
exit raw r; // 42
};
The fat pointer occupies 16 bytes in the struct layout regardless of the
concrete type. Field-level borrows through dyn are not yet supported —
borrow the whole struct instead. If no impl of the trait exists in scope,
the assignment emits ARIA-043.
See traits/dyn.md for the full dyn Trait reference.