A struct is a type made from named fields.
const Point = struct {
x: i32,
y: i32,
};This declares a type named Point. It has two fields, x and y. Both fields have type i32.
A value of this type is written with a struct literal:
const p = Point{
.x = 10,
.y = 20,
};The field names are part of the literal. This makes the code clear when a struct has many fields.
A complete program:
const std = @import("std");
const Point = struct {
x: i32,
y: i32,
};
pub fn main() void {
const p = Point{
.x = 10,
.y = 20,
};
std.debug.print("x = {d}, y = {d}\n", .{ p.x, p.y });
}The output is:
x = 10, y = 20Fields are selected with the dot operator:
p.x
p.yThe dot is used both when constructing a value and when reading a field. In a literal, .x = 10 means “set the field named x.” In an expression, p.x means “read the field x from p.”
A struct value may be mutable.
const std = @import("std");
const Point = struct {
x: i32,
y: i32,
};
pub fn main() void {
var p = Point{
.x = 10,
.y = 20,
};
p.x = 30;
std.debug.print("x = {d}, y = {d}\n", .{ p.x, p.y });
}The output is:
x = 30, y = 20The variable p is declared with var, so its fields may be changed. If p were declared with const, the assignment to p.x would be rejected.
A struct can contain fields of different types.
const User = struct {
id: u64,
name: []const u8,
active: bool,
};Here name is a slice of constant bytes. In small programs, this is the usual type for a string value.
const user = User{
.id = 1,
.name = "alice",
.active = true,
};The type of "alice" fits []const u8.
Struct declarations may be nested inside other declarations.
const std = @import("std");
pub fn main() void {
const Pair = struct {
left: i32,
right: i32,
};
const pair = Pair{
.left = 3,
.right = 4,
};
std.debug.print("{d}, {d}\n", .{ pair.left, pair.right });
}A struct type can also be anonymous. This is useful for small temporary values.
const value = .{
.name = "zig",
.year = 2026,
};Here Zig infers an anonymous struct type. The value has fields named name and year.
Anonymous structs are often used as arguments to formatting functions:
std.debug.print("{s} {d}\n", .{ "Zig", 2026 });The expression .{ "Zig", 2026 } is also an anonymous struct, but with numbered fields instead of named fields. This form is called a tuple.
A struct may have default field values.
const Config = struct {
verbose: bool = false,
retries: u8 = 3,
};Then a literal may omit those fields:
const config = Config{};This is the same as:
const config = Config{
.verbose = false,
.retries = 3,
};A literal may override only the fields it needs:
const config = Config{
.verbose = true,
};Now verbose is true and retries is still 3.
Structs may contain declarations as well as fields.
const Point = struct {
x: i32,
y: i32,
const zero = Point{
.x = 0,
.y = 0,
};
};The declaration zero belongs to the namespace of Point.
It is accessed with:
Point.zeroA struct is both a layout of data and a namespace. This is important in Zig. Functions associated with a type are just declarations inside the struct. There is no separate class system.
For now, use a struct when several values should move together under one name.
const Rectangle = struct {
width: u32,
height: u32,
};This is clearer than passing two unrelated integers through the program.
Exercises.
7-1. Define a struct named Book with fields title, pages, and published.
7-2. Create a Book value and print its fields.
7-3. Define a struct named Counter with one field, value: u32. Make a mutable counter and increment it.
7-4. Define a Config struct with default values. Create one value using all defaults, and another value overriding one field.