// 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 expectEqual(0.0, v1.dot(v2)); // 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 expectEqual(0.0, Vec3.dot(v1, v2)); } // Structs can have declarations. // Structs can have 0 fields. const Empty = struct { pub const PI = 3.14; }; test "struct namespaced variable" { try expectEqual(3.14, Empty.PI); try expectEqual(0, @sizeOf(Empty)); // 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 expectEqual(0.9, point.y); } // 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 expectEqual(LinkedList(i32), LinkedList(i32)); const list = LinkedList(i32){ .first = null, .last = null, .len = 0, }; try expectEqual(0, list.len); // Since types are first class values you can instantiate the type // by assigning it to a variable: const ListOfInts = LinkedList(i32); try expectEqual(LinkedList(i32), ListOfInts); 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 expectEqual(1234, list2.first.?.data); // instead of try expectEqual(1234, list2.first.?.*.data); } const expectEqual = @import("std").testing.expectEqual; // test