// Declare a struct. // Zig gives no guarantees about the order of fields and the size of // the struct but the fields are guaranteed to be ABI-aligned. const Point = struct { x: f32, y: f32, }; // Declare an instance of a struct. const p: Point = .{ .x = 0.12, .y = 0.34, }; // Functions in the struct's namespace can be called with dot syntax. const Vec3 = struct { x: f32, y: f32, z: f32, pub fn init(x: f32, y: f32, z: f32) Vec3 { return Vec3{ .x = x, .y = y, .z = z, }; } pub fn dot(self: Vec3, other: Vec3) f32 { return self.x * other.x + self.y * other.y + self.z * other.z; } }; test "dot product" { const v1 = Vec3.init(1.0, 0.0, 0.0); const v2 = Vec3.init(0.0, 1.0, 0.0); try expect(v1.dot(v2) == 0.0); // Other than being available to call with dot syntax, struct methods are // not special. You can reference them as any other declaration inside // the struct: try expect(Vec3.dot(v1, v2) == 0.0); } // Structs can have declarations. // Structs can have 0 fields. const Empty = struct { pub const PI = 3.14; }; test "struct namespaced variable" { try expect(Empty.PI == 3.14); try expect(@sizeOf(Empty) == 0); // Empty structs can be instantiated the same as usual. const does_nothing: Empty = .{}; _ = does_nothing; } // Struct field order is determined by the compiler, however, a base pointer // can be computed from a field pointer: fn setYBasedOnX(x: *f32, y: f32) void { const point: *Point = @fieldParentPtr("x", x); point.y = y; } test "field parent pointer" { var point = Point{ .x = 0.1234, .y = 0.5678, }; setYBasedOnX(&point.x, 0.9); try expect(point.y == 0.9); } // Structs can be returned from functions. fn LinkedList(comptime T: type) type { return struct { pub const Node = struct { prev: ?*Node, next: ?*Node, data: T, }; first: ?*Node, last: ?*Node, len: usize, }; } test "linked list" { // Functions called at compile-time are memoized. try expect(LinkedList(i32) == LinkedList(i32)); const list = LinkedList(i32){ .first = null, .last = null, .len = 0, }; try expect(list.len == 0); // Since types are first class values you can instantiate the type // by assigning it to a variable: const ListOfInts = LinkedList(i32); try expect(ListOfInts == LinkedList(i32)); var node = ListOfInts.Node{ .prev = null, .next = null, .data = 1234, }; const list2 = LinkedList(i32){ .first = &node, .last = &node, .len = 1, }; // When using a pointer to a struct, fields can be accessed directly, // without explicitly dereferencing the pointer. // So you can do try expect(list2.first.?.data == 1234); // instead of try expect(list2.first.?.*.data == 1234); } const expect = @import("std").testing.expect; // test