struct Methods and Associated Functions
In Zig, a struct can contain not only fields but also methods and associated functions. The pub keyword controls access visibility, and taking self as the first parameter makes a function callable as an instance method. This page covers how to define methods, the role of pub, and how to use self.
Syntax
// -----------------------------------------------
// Defining methods on a struct and using pub / self
// -----------------------------------------------
const TypeName = struct {
field_name: Type,
// -----------------------------------------------
// Immutable method — receives self by value
// Can only read fields; cannot modify them
// -----------------------------------------------
pub fn methodName(self: TypeName) ReturnType {
return self.field_name;
}
// -----------------------------------------------
// Mutable method — receives self as a pointer
// Must use pointer passing to modify fields
// -----------------------------------------------
pub fn mutableMethodName(self: *TypeName, arg: Type) void {
self.field_name = arg;
}
// -----------------------------------------------
// Associated function (constructor equivalent) — does not receive self
// Called as TypeName.functionName()
// -----------------------------------------------
pub fn init(arg: Type) TypeName {
return TypeName{
.field_name = arg,
};
}
// -----------------------------------------------
// Methods without pub are accessible only within the same file
// -----------------------------------------------
fn privateMethodName(self: TypeName) ReturnType {
return self.field_name;
}
};
// Calling methods
var instance = TypeName.init(value); // calling an associated function
const result = instance.methodName(); // calling an immutable method
instance.mutableMethodName(value); // calling a mutable method
Syntax reference
| Syntax / method | Description |
|---|---|
pub fn method(self: T) Type | Defines an immutable instance method. self is passed by value and can only read fields. |
pub fn method(self: *T) void | Defines a mutable instance method. self is passed as a pointer, allowing fields to be modified. |
pub fn init(...) T | Defines an associated function that works as a constructor equivalent. It does not receive self and is called as TypeName.init(). |
fn method(self: T) Type | Defines a private method without pub. Accessible only from within the same file. |
self.fieldName | Reads a field of the current instance from within a method. |
self.fieldName = value | Modifies a field from within a mutable method. Requires pointer passing with self: *T. |
self.otherMethod() | Calls another method on the same struct from within a method. |
TypeName.associatedFn() | Calls an associated function without an instance. |
pub | Access modifier that makes a function, method, or field accessible from other files. |
Sample code
steinsgate_method.zig
// steinsgate_method.zig — Uses Steins;Gate characters to demonstrate
// struct methods, pub, and self in Zig
// Import the standard library
const std = @import("std");
// -----------------------------------------------
// LabMember struct — defines a type representing a lab member
// -----------------------------------------------
const LabMember = struct {
name: []const u8, // lab member's name
lab_no: u8, // lab member number
divergence: f64, // current world line divergence value
is_future: bool, // flag indicating a time traveler from the future
// -----------------------------------------------
// init — associated function that acts as a constructor
// Does not receive self; called as TypeName.init()
// -----------------------------------------------
pub fn init(name: []const u8, lab_no: u8, divergence: f64) LabMember {
return LabMember{
.name = name,
.lab_no = lab_no,
.divergence = divergence,
.is_future = false, // not a time traveler by default
};
}
// -----------------------------------------------
// label — immutable method that returns the lab member number as a string
// self: LabMember (by value) — reads fields only
// -----------------------------------------------
pub fn label(self: LabMember) []const u8 {
return switch (self.lab_no) {
1 => "Lab Member 001",
2 => "Lab Member 002",
3 => "Lab Member 003",
4 => "Lab Member 004",
5 => "Lab Member 005",
else => "Lab Member ???",
};
}
// -----------------------------------------------
// jump — mutable method that moves to a different world line
// self: *LabMember (pointer) — modifies fields
// -----------------------------------------------
pub fn jump(self: *LabMember, new_divergence: f64) void {
self.divergence = new_divergence;
}
// -----------------------------------------------
// mark_as_future — mutable method that sets the future traveler flag
// -----------------------------------------------
pub fn mark_as_future(self: *LabMember) void {
self.is_future = true;
}
// -----------------------------------------------
// describe — method that prints member information
// Calls label() internally to get the lab member number string
// -----------------------------------------------
pub fn describe(self: LabMember, writer: anytype) !void {
const future_mark = if (self.is_future) " [Future]" else "";
try writer.print(
" {s}: {s} — Divergence: {d:.6}{s}\n",
.{ self.label(), self.name, self.divergence, future_mark },
);
}
// -----------------------------------------------
// is_steins_gate — private method (no pub) that returns whether
// the current world line is Steins;Gate
// Can only be called from within the same file
// -----------------------------------------------
fn is_steins_gate(self: LabMember) bool {
// The Steins;Gate world line divergence value is 1.048596
return self.divergence >= 1.048596 and self.divergence < 1.048597;
}
};
// -----------------------------------------------
// TimeLeap struct — defines a type representing the time leap machine
// -----------------------------------------------
const TimeLeap = struct {
model: []const u8, // device name
jumps: u32, // number of time leaps performed
// Creates an instance using the init associated function
pub fn init(model: []const u8) TimeLeap {
return TimeLeap{
.model = model,
.jumps = 0,
};
}
// execute — performs a time leap and increments the counter
pub fn execute(self: *TimeLeap) void {
self.jumps += 1;
}
// status — prints the current state as a string
pub fn status(self: TimeLeap, writer: anytype) !void {
try writer.print(" {s}: Time leaps performed: {d}\n", .{ self.model, self.jumps });
}
};
pub fn main() !void {
// Get a writer for standard output
const stdout = std.io.getStdOut().writer();
try stdout.print("=== Steins;Gate Lab Member Management System ===\n\n", .{});
// -----------------------------------------------
// Create instances using the init associated function
// Declare with var so mutable methods can be called
// -----------------------------------------------
var okabe = LabMember.init("Okabe Rintaro", 1, 0.571024);
var kurisu = LabMember.init("Makise Kurisu", 2, 0.571024);
var mayuri = LabMember.init("Shiina Mayuri", 3, 0.571024);
var hashida = LabMember.init("Hashida Itaru", 4, 0.571024);
var moeka = LabMember.init("Kiryu Moeka", 5, 0.571024);
// Set the future traveler flag using mark_as_future (pointer-passing method)
kurisu.mark_as_future();
// -----------------------------------------------
// Display member information using describe()
// -----------------------------------------------
try stdout.print("--- Initial world line members (alpha line 0.571024) ---\n", .{});
try okabe.describe(stdout);
try kurisu.describe(stdout);
try mayuri.describe(stdout);
try hashida.describe(stdout);
try moeka.describe(stdout);
// -----------------------------------------------
// Move to a different world line using the jump() mutable method
// -----------------------------------------------
try stdout.print("\n--- After world line shift (transitioning to beta line) ---\n", .{});
okabe.jump(1.130205);
kurisu.jump(1.130205);
mayuri.jump(1.130205);
hashida.jump(1.130205);
moeka.jump(1.130205);
try okabe.describe(stdout);
try kurisu.describe(stdout);
try mayuri.describe(stdout);
try hashida.describe(stdout);
try moeka.describe(stdout);
// -----------------------------------------------
// Move to another world line, aiming for Steins;Gate
// -----------------------------------------------
try stdout.print("\n--- Reaching Steins;Gate ---\n", .{});
okabe.jump(1.048596);
// is_steins_gate() is a private method, but it's callable
// because it's in the same file
if (okabe.is_steins_gate()) {
try stdout.print(" Divergence {d:.6} — El Psy Kongroo\n", .{okabe.divergence});
}
try okabe.describe(stdout);
// -----------------------------------------------
// Create and use an instance of TimeLeap
// -----------------------------------------------
try stdout.print("\n--- Time Leap Machine Operation Log ---\n", .{});
var machine = TimeLeap.init("Phone Microwave (Name Subject to Change)");
// Call execute() multiple times to accumulate the count
machine.execute();
machine.execute();
machine.execute();
try machine.status(stdout);
}
zig run steinsgate_method.zig === Steins;Gate Lab Member Management System === --- Initial world line members (alpha line 0.571024) --- Lab Member 001: Okabe Rintaro — Divergence: 0.571024 Lab Member 002: Makise Kurisu — Divergence: 0.571024 [Future] Lab Member 003: Shiina Mayuri — Divergence: 0.571024 Lab Member 004: Hashida Itaru — Divergence: 0.571024 Lab Member 005: Kiryu Moeka — Divergence: 0.571024 --- After world line shift (transitioning to beta line) --- Lab Member 001: Okabe Rintaro — Divergence: 1.130205 Lab Member 002: Makise Kurisu — Divergence: 1.130205 [Future] Lab Member 003: Shiina Mayuri — Divergence: 1.130205 Lab Member 004: Hashida Itaru — Divergence: 1.130205 Lab Member 005: Kiryu Moeka — Divergence: 1.130205 --- Reaching Steins;Gate --- Divergence 1.048596 — El Psy Kongroo Lab Member 001: Okabe Rintaro — Divergence: 1.048596 --- Time Leap Machine Operation Log --- Phone Microwave (Name Subject to Change): Time leaps performed: 3
Overview
Struct methods in Zig are defined by placing functions inside a struct block. Specifying self: TypeName (by value) as the first parameter creates an immutable method that can only read fields. Specifying self: *TypeName (by pointer) creates a mutable method that can modify fields. To call a mutable method, the instance must be declared with var rather than const.
The pub keyword is an access modifier. Defining a function as pub fn makes it accessible from other files. Methods without pub are private and can only be used within the same file. Functions that do not take self as the first parameter are called "associated functions" and can be called without an instance as TypeName.functionName(). Using the init() pattern to implement a constructor is idiomatic in Zig. From within a method, you can call other methods on the same struct using self.methodName(). For the basics of structs, see struct basics.
If you find any errors or copyright issues, please contact us.