Skip to Content

Struct

struct is a composite type with value semantics: assignment and argument passing copy the whole thing, and changing a copy leaves the original untouched. A good fit for small, self-contained data such as coordinates or config entries.

Declaration and Construction

struct Point { public var x: int = 0; public var y: int = 0; public func Show() -> string => $"({x}, {y})"; } struct Pair { public val a: string; public val b: string; init(a: string, b: string) { self.a = a; self.b = b; } }

Construction goes through new: a struct without a constructor can be created directly with new Point(), yielding an instance with all fields at their zero values; a struct with a constructor requires arguments matching its signature:

val p = new Point(); print(p.Show()); val q = new Point() { x: 3, y = 4 }; print(q.Show()); val pr = new Pair("a", "b");
(0, 0) (3, 4)

The object initializer { field: value } (: or = both work) assigns field by field after construction. Fields declared with an initializer start from the declared value; the rest take the type’s zero value.

Value Semantics

val q = new Point() { x: 3, y: 4 }; var r = q; r.x = 99; print(q.x, r.x);
3 99

r = q copies the whole value; the two variables are independent afterwards. This is the core difference between struct and class: put the same code on a class and both sides end up 99.

Value Semantics in Containers

Structs keep value semantics inside containers too: what you read from an index is a copy, and changing the copy does not write back. The one exception is the index-position direct field write, which targets the container element itself:

var xs = new list<Point>(); xs.Add(new Point() { x: 1 }); xs[0].x = 5; print(xs[0].x); var p = xs[0]; p.x = 9; print(xs[0].x, p.x);
5 5 9

xs[0].x = 5 writes into the container; var p = xs[0] copies out first, so the later p.x = 9 changes only the copy.

Reference-type fields inside a nested struct (class references, containers, and so on) still copy the reference itself, pointing at the same object.

Fields and Methods

  • Fields have three kinds of binding: val (immutable; every explicit constructor must assign it), var (mutable), and const (compile-time constant). Fields without a modifier default to private.
  • Methods take self as their first parameter (or use a bare self); inside the body, the struct’s own members are accessed directly by field name.
  • The primary constructor form struct Name(args) { } promotes the parameters to private val fields, saving you from writing an init by hand.

Constructor Delegation

Constructors delegate to each other with : init(...), avoiding duplicated assignment logic:

struct Gauge { public val id: int; public var hits: int = 0; init(id: int) { self.id = id; } init() : init(0) { } } val c = new Gauge(7); print(c.id, c.hits); val c0 = new Gauge(); print(c0.id);
7 0 0

The primary constructor form struct Gauge(id: int) promotes the parameters to private val fields, which suits pure data aggregation; write an explicit init when you need logic.

with: Non-Destructive Updates

Immutable data structures derive new values with with instead of mutating the original instance. with chains: each level produces a new copy:

struct Point3 { public var x: int = 0; public var y: int = 0; public var z: int = 0; } val q = new Point() { x: 3, y: 4 }; val moved = q with { x: 10 }; print(q.Show(), moved.Show()); val p3 = new Point3() { x: 1, y: 2, z: 3 }; val stepped = p3 with { x: 10 } with { y: 20 }; print(stepped.x, stepped.y, stepped.z); val half = new Point3() { z: 9 } with { y: 5 }; print(half.x, half.y, half.z);
(3, 4) (10, 4) 10 20 3 0 5 9

Fields missing from the initializer take the type’s zero value (half’s x is 0), and with fills in level by level. with also supports chaining and nested blocks; see Vectors and Colors for the same semantics.

Notes

  • Generic structs struct Box<T> { }, protocol implementations struct Tag impl Greeter, and constraints where T: ... all compose with struct.
  • A constructor can declare a fail clause; new Gauge(args) is then a fallible expression and the call site must consume it; see Failure.
  • Structs have no inheritance; reuse behavior through protocols and mixins.
Last updated on October 11, 2026