Structs
Basic structs
struct Point {
x: f64;
y: f64;
}
let origin = Point { x: 0.0, y: 0.0 };Field separators accept both , and ; in declarations and literals.
A literal names every field. Leaving one out is a compile error that names every field missing:
let p = Point { x: 3.0 };
// error: Point { ... } leaves out y (give it, add '..' to zero the rest, or declare a default)Field defaults
A field can declare a default, which a literal that leaves it out gets:
struct Window {
title: string; // required
width: i64 = 800;
height: i64 = 600;
resizable: bool = true;
icon: ?string = null;
}
let w = Window { title: "Notes"; height: 400; }; // width 800, resizable, no iconA default is a constant of the field’s type: a literal (number, string,
bool, color, null), [] for an empty slice, an array literal of these
for a fixed array (up: [3]f64 = [0.0, 1.0, 0.0];), an enum variant, a
const (an array const too), or integer arithmetic on them
(= PAGE * 4). It is evaluated once, at
compile time, where the struct is declared, so another module’s literal gets
the same value. Defaults can be on any field, in any order. Adding a field
with a default to a struct keeps every existing literal compiling.
..: zero the rest
A last .. zeroes every field the literal leaves out that has no default:
0, false, null, an empty slice or string. A defaulted field still gets
its default.
let p = Point { x: 3.0; .. }; // y is 0.0
var wc = WNDCLASSEXW { cbSize: 80; lpfnWndProc: wnd_proc; .. };
let w = Window { title: "x"; .. }; // width 800: defaults still applyUse .. where zero is deliberate for many fields (OS and C structs, scratch
state); name a field where its value matters; give a field whose zero is its
natural value a default. var p: Point; (a declaration without a literal)
is still all zero. Tagged-union payloads follow the same rules:
Ev.Key { code: 1; .. }.
Field access
let px = origin.x;Methods
Use Type.name syntax to declare methods:
struct Vec2 { x: f64; y: f64; }
fn Vec2.length(*const self) -> f64 {
return math.sqrt_f64(self.x * self.x + self.y * self.y);
}
fn Vec2.normalize(*const self) -> Vec2 {
let len = self.length();
return Vec2 { x: self.x / len, y: self.y / len };
}
fn Vec2.flip(*self) {
self.x = -self.x;
}The first parameter is self (by value), *self (by pointer, may change
the struct: needs a var) or *const self (by read-only pointer: works on
a let too). See Methods.
A method is found by the receiver’s type alone: v.m() is m of v’s type,
whether v is a local, a constant (UP.extend(0.0), geo.UP.extend(0.0)), a
field, an element or a call’s result. A type with no m is a compile error,
never another type’s m.
Swizzle access
Structs can declare swizzle patterns with @swizzle to enable GLSL-style field access:
@swizzle xy: Vec2;
@swizzle xyz: Vec3;
struct Vec4 { x: f64; y: f64; z: f64; w: f64; }
let v = Vec4 { x: 1.0, y: 2.0, z: 3.0, w: 4.0 };
let xy = v.xy; // Vec2 { x: 1.0, y: 2.0 }
let zw = v.zw; // Vec2 { x: 3.0, y: 4.0 }: fields fill in orderThe @swizzle attribute maps a field-name pattern to a result type. It cannot be used with @packed or @opaque structs.
A swizzle works on any value of the struct, not only a variable: a field
(box.v.xy), a call’s result (corner().yx), an operator’s result
((a + b).zw).
Struct attributes
@packed
struct PacketHeader {
magic: u32;
length: u16;
flags: u8;
}
@align(16)
struct SimdVec {
data: [4]f32;
}
@opaque
struct Handle {
id: u64;
}-
@packed— no padding between fields (alignment 1):PacketHeaderis 7 bytes,lengthat offset 4 andflagsat 6, like C’s#pragma pack(1). Unaligned fields read and write normally. It packs bytes, not bits. -
@align(N)— minimum alignment (a power of two, at most 16):SimdVec’s alignment is 16 and its size rounds up to a multiple of 16. Locals, heap values and globals of the type are 16-byte aligned, as C’s_Alignas(16). -
@opaque— fields not visible outside the module
@sizeof and @alignof give the same numbers in @tape code and at runtime.
Tape has no bitfields: a field is as wide as its type (flags: u8 : 3 and
u3 are errors). C leaves bitfield layout up to each compiler, so it can’t
describe a register or wire format reliably. Declare the integer that holds
the bits and use shifts and masks:
@packed
struct UartRegs { data: u32; status: u32; } // status: [0] ready, [1..4] level
fn ready(r: *const UartRegs) -> bool { return (r.status & 1) != 0; }
fn level(r: *const UartRegs) -> u32 { return (r.status >> 1) & 0x7; }Last modified: