Skip to content

Field Access

Fields are selected with the dot operator.

Fields are selected with the dot operator.

point.x
point.y

The expression on the left must be a struct, union, enum, or pointer to one of those types. The name on the right selects a field or declaration.

Begin with a small example.

const std = @import("std");

const Point = struct {
    x: i32,
    y: i32,
};

pub fn main() void {
    const point = Point{
        .x = 4,
        .y = 7,
    };

    std.debug.print("{d}\n", .{point.x});
    std.debug.print("{d}\n", .{point.y});
}

The output is:

4
7

point.x reads the field named x from point.

A field may be assigned if the struct value is mutable.

const std = @import("std");

const Counter = struct {
    value: u32,
};

pub fn main() void {
    var counter = Counter{
        .value = 0,
    };

    counter.value = 10;

    std.debug.print("{d}\n", .{counter.value});
}

The output is:

10

If counter were declared with const, the assignment would fail.

const counter = Counter{
    .value = 0,
};

counter.value = 10;

Zig would report that the value is constant.

Struct assignment copies the entire value.

const std = @import("std");

const Point = struct {
    x: i32,
    y: i32,
};

pub fn main() void {
    var a = Point{
        .x = 1,
        .y = 2,
    };

    var b = a;

    b.x = 99;

    std.debug.print("a.x = {d}\n", .{a.x});
    std.debug.print("b.x = {d}\n", .{b.x});
}

The output is:

a.x = 1
b.x = 99

Changing b does not change a. The assignment copied the struct value.

A pointer may also be used to access fields.

const std = @import("std");

const Point = struct {
    x: i32,
    y: i32,
};

pub fn main() void {
    var point = Point{
        .x = 3,
        .y = 5,
    };

    const ptr = &point;

    ptr.x = 100;

    std.debug.print("{d}\n", .{point.x});
}

The output is:

100

Here ptr is a pointer to Point.

Notice that the code uses:

ptr.x

not:

(*ptr).x

Zig automatically dereferences pointers when selecting fields. This removes much of the punctuation common in C programs.

Field access works through nested structs.

const std = @import("std");

const Size = struct {
    width: u32,
    height: u32,
};

const Window = struct {
    title: []const u8,
    size: Size,
};

pub fn main() void {
    const window = Window{
        .title = "editor",
        .size = Size{
            .width = 800,
            .height = 600,
        },
    };

    std.debug.print("{d} x {d}\n", .{
        window.size.width,
        window.size.height,
    });
}

The output is:

800 x 600

Each dot selects one field.

A field may itself be another struct, array, slice, or pointer. Field access works the same way in each case.

Struct declarations can also contain constants and functions.

const Vec2 = struct {
    x: f32,
    y: f32,

    const zero = Vec2{
        .x = 0,
        .y = 0,
    };
};

The constant is accessed with:

Vec2.zero

This also uses the dot operator.

Functions inside a struct are accessed the same way.

const std = @import("std");

const Counter = struct {
    value: u32,

    pub fn reset(counter: *Counter) void {
        counter.value = 0;
    }
};

pub fn main() void {
    var counter = Counter{
        .value = 25,
    };

    Counter.reset(&counter);

    std.debug.print("{d}\n", .{counter.value});
}

The output is:

0

Counter.reset selects the function declaration named reset from the namespace of Counter.

A field name must exist.

point.z

This is an error because Point has no field named z.

Zig checks field names at compile time. Misspelled names are reported immediately.

Exercises.

7-5. Define a Person struct with fields name and age. Print both fields.

7-6. Make a mutable struct value and change one field.

7-7. Create a nested struct representing a rectangle with a position and size.

7-8. Copy a struct into another variable. Change one field in the copy and verify that the original value does not change.