Structs
A struct is a user-defined data type used to group
variables into a single type. Additionally, structs may define methods
(member functions), that can be called on instances of the struct.
Inside methods, this is a non-null reference
(T&) to the instance that the function was called on.
Use &this when a storable T* is required.
Methods must be defined inside the struct or enum body.
struct Person {
string name;
int age;
Person*? friend;
void introduce() {
// Members may be accessed via 'this' or directly.
println(this.name, " is ", age, " years old");
}
}
void main() {
var alice = Person(name = "Alice", age = 30, friend = null);
var bob = Person(name = "Bob", age = 25, friend = &alice);
alice.friend = &bob;
alice.introduce(); // prints "Alice is 30 years old"
bob.introduce(); // prints "Bob is 25 years old"
}Constructors
A struct without an explicit constructor gets an autogenerated one that takes each field as a parameter, positionally or by name:
struct Point {
int x;
int y;
}
void main() {
var a = Point(1, 2);
var b = Point(x = 3, y = 4);
println(a.x + b.x); // prints 4
}Named arguments can be given in any order and can skip fields with default values. Argument expressions always evaluate in the order written.
To customize construction, declare a constructor: a method with the same name as the struct. It must initialize every field without a default value; fields with defaults are initialized automatically unless the constructor plainly assigns them before reading them. Defaults evaluate in field order before the body runs.
Field default scope
Like parameter defaults, they resolve where they are declared: names in a default never capture locals at the construction site.
Use this. to refer to fields shadowed by parameters. A
constructor can delegate to another with a top-level
init(...) call;
Constructor delegation edge cases
The target performs initialization, so the delegating body needs no
assignments of its own. Calling init(...) on another
instance instead reinitializes that instance in place. Overwriting a
delegation-built field after the init(...) call destroys
the old value first. Storing to a field before the
init(...) call is an error when the delegation provably
overwrites it: such stores can never be observed.
Constructors are never implicit: a single-parameter constructor does
not convert its argument type, so pass Wrapper(5)
explicitly instead of a bare 5:
struct Counter {
int count;
Counter(int count) {
this.count = count;
}
}
void main() {
var counter = Counter(10);
println(counter.count); // prints 10
}A constructor marked implicit also acts as a conversion
from its argument type; see Implicit conversions.
See Move semantics for how values are copied and moved.
Printing
Structs print generically: print renders the struct name
followed by each field, so no boilerplate is needed for logging and
debugging. Calling .toString() works too: both are provided
automatically, with no : Printable declaration needed.
struct Point {
int x;
int y;
}
void main() {
println(Point(1, 2)); // prints "Point(x: 1, y: 2)"
}Nested structs, generic structs instantiated with printable
arguments, and containers of structs all work. Nullable fields print as
null when null.
Custom print methods
A custom print method overrides the compiler-generated one. A
hand-written print alone does not make
print(value) work: like before, the struct must still
declare : Printable for that.