Structs

12.1 Defining and Using Structs

Methods are just namespaced functions: p.length() ≡ Point.length(p).

Decl literals (0.14+): const p: Point = .init(3, 4); or .origin resolve against the result type.

const Point = struct {
    x: f32,
    y: f32 = 0,                          // default field value

    pub const origin = Point{ .x = 0, .y = 0 };   // declaration (namespaced constant)

    pub fn init(x: f32, y: f32) Point {  // "constructor" convention
        return .{ .x = x, .y = y };
    }

    pub fn length(self: Point) f32 {     // method (self by value)
        return @sqrt(self.x * self.x + self.y * self.y);
    }

    pub fn scale(self: *Point, k: f32) void {   // mutating method (self by pointer)
        self.x *= k;
        self.y *= k;
    }
};

var p = Point.init(3, 4);
p.scale(2);
std.debug.print("{d}\n", .{p.length()});
const q: Point = .{ .x = 1 };            // anonymous struct literal with an inferred type
const r = Point.origin;
_ = .{ q, r };

12.2 Struct Memory Layout

struct (auto) The compiler may reorder fields for optimal size. No guaranteed layout.

extern struct C ABI layout (field order kept, C alignment rules) packed struct

Bit-packed with no padding. Fields can be arbitrary-width ints. Backed by an integer.

const Flags = packed struct(u8) {         // exactly 8 bits
    read: bool,
    write: bool,
    exec: bool,
    _padding: u5 = 0,
};
const f = Flags{ .read = true, .write = false, .exec = true };
const raw: u8 = @bitCast(f);
_ = raw;

const CHeader = extern struct {
    magic: u32,
    version: u16,
    flags: u16,
};

12.3 Anonymous Structs and Tuples

const anon = .{ .name = "Ana", .age = 30 };         // anonymous struct literal
const tuple = .{ 1, "two", 3.0 };                   // tuple (fields named 0, 1, 2)
std.debug.print("{s} {d}\n", .{ anon.name, tuple[0] });
const Pair = struct { i32, []const u8 };            // tuple type
const pr: Pair = .{ 1, "x" };
_ = pr;
inline for (tuple) |item| std.debug.print("{any}\n", .{item});

12.4 Files as Structs

// Vec2.zig: a file with top-level fields IS a struct type
x: f32,
y: f32,

const Vec2 = @This();

pub fn add(a: Vec2, b: Vec2) Vec2 {
    return .{ .x = a.x + b.x, .y = a.y + b.y };
}
// main.zig
const Vec2 = @import("Vec2.zig");
const v = Vec2.add(.{ .x = 1, .y = 2 }, .{ .x = 3, .y = 4 });

12.5 @This() and Self-Reference

const Node = struct {
    value: i32,
    next: ?*Node = null,          // self-referential via pointer
    const Self = @This();
    pub fn append(self: *Self, n: *Node) void { self.next = n; }
};

12.6 Struct Equality and Printing

Structs don't support ==. Compare fields, or use std.meta.eql(a, b).

Printing: {any} prints a debug representation. Define a format method for custom formatting (Module 20).

12.7 Field Parent Pointer

This is the basis of intrusive data structures (e.g., std.DoublyLinkedList in 0.15) and interface patterns.

const Outer = struct { id: u32, inner: Inner };
fn fromInner(inner_ptr: *Inner) *Outer {
    return @fieldParentPtr("inner", inner_ptr);   // recover the containing struct
}