🎓 A1Learner
🔍
0 of 46 lessons completed

Zig Struct Methods in Depth

A struct method is a function declared inside a struct that operates on a value of that struct. It keeps the behaviour of a type next to its data.

Changing a value

A pointer self lets a method change the value. Two calls raise the count from 10 to 12.

const std = @import("std");
const Counter = struct {
    count: i32,
    fn inc(self: *Counter) void {
        self.count += 1;
    }
    fn get(self: Counter) i32 {
        return self.count;
    }
};
pub fn main() !void {
    const out = std.io.getStdOut().writer();
    var c = Counter{ .count = 10 };
    c.inc();
    c.inc();
    try out.print("counter={d}\n", .{c.get()});
}

Returning a new value

A value self reads the value without changing it. The add method returns a fresh point.

const std = @import("std");
const Point = struct {
    x: i32,
    y: i32,
    fn add(self: Point, other: Point) Point {
        return Point{ .x = self.x + other.x, .y = self.y + other.y };
    }
    fn sum(self: Point) i32 {
        return self.x + self.y;
    }
};
pub fn main() !void {
    const out = std.io.getStdOut().writer();
    const p = Point{ .x = 3, .y = 4 };
    const q = Point{ .x = 10, .y = 20 };
    const r = p.add(q);
    try out.print("r x={d} y={d} sum={d}\n", .{ r.x, r.y, r.sum() });
}

Building a new value

A method without self builds a new value. It is called on the type name, not on a value.

const std = @import("std");
const Counter = struct {
    count: i32,
    fn withCount(n: i32) Counter {
        return Counter{ .count = n };
    }
    fn get(self: Counter) i32 {
        return self.count;
    }
};
pub fn main() !void {
    const out = std.io.getStdOut().writer();
    const d = Counter.withCount(7);
    try out.print("made={d}\n", .{d.get()});
}

Example

const std = @import("std");
const Rect = struct {
    width: i32,
    height: i32,
    fn area(self: Rect) i32 {
        return self.width * self.height;
    }
    fn grow(self: *Rect, factor: i32) void {
        self.width *= factor;
        self.height *= factor;
    }
};
pub fn main() !void {
    const out = std.io.getStdOut().writer();
    var r = Rect{ .width = 3, .height = 4 };
    try out.print("area={d}\n", .{r.area()});
    r.grow(2);
    try out.print("grown area={d} width={d} height={d}\n", .{ r.area(), r.width, r.height });
}

Key points

  • Struct method: a struct method is a function written inside a struct definition.
  • Self: self is the first parameter of a method, and it names the struct value the method works on.
  • Pointer self: a pointer self is a self parameter written as *Self, and it lets a method change the struct value.
  • Value self: a value self is a self parameter written as Self, and it lets a method read the value without changing the original.
  • Factory method: a factory method is a method that builds and returns a new struct value, and it has no self parameter.
  • A method with pointer self changes the original value, while a method with value self leaves it unchanged.

The area method reads width and height. The grow method takes pointer self and changes both fields. After growing by 2, the area rises from 12 to 48.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

Which self parameter lets a method change the original struct value?