🎓 A1Learner
🔍
0 of 55 lessons completed

Zig Generic Structs

A generic struct is a struct built from a type passed at compile time. A small function returns the struct type, so one pattern can hold many kinds of value.

A Pair holds two values of the chosen type

Pair is a function that returns a struct type. Calling it with i32 builds a pair of numbers, and calling it with a string type builds a pair of words.

const std = @import("std");

fn Pair(comptime T: type) type {
    return struct {
        first: T,
        second: T,
    };
}

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    const nums = Pair(i32){ .first = 3, .second = 7 };
    const words = Pair([]const u8){ .first = "red", .second = "blue" };
    try out.print("{d}\n", .{nums.first});
    try out.print("{d}\n", .{nums.second});
    try out.print("{s}\n", .{words.first});
    try out.print("{s}\n", .{words.second});
}

Push adds to a stack and pop takes the last value

The Stack factory builds a struct with a fixed array of four slots and a count. The count tracks how many slots are in use.

const std = @import("std");

fn Stack(comptime T: type) type {
    return struct {
        items: [4]T = undefined,
        count: usize = 0,

        pub fn push(self: *@This(), value: T) void {
            self.items[self.count] = value;
            self.count += 1;
        }

        pub fn pop(self: *@This()) ?T {
            if (self.count == 0) return null;
            self.count -= 1;
            return self.items[self.count];
        }
    };
}

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    var stack = Stack(i32){};
    stack.push(10);
    stack.push(20);
    try out.print("{d}\n", .{stack.count});
    try out.print("{?}\n", .{stack.pop()});
    try out.print("{d}\n", .{stack.count});
}

A stack can hold words, and an empty pop gives null

The same factory builds a stack of words with no new code. Popping a stack that holds nothing returns null, because pop returns an optional.

const std = @import("std");

fn Stack(comptime T: type) type {
    return struct {
        items: [4]T = undefined,
        count: usize = 0,

        pub fn push(self: *@This(), value: T) void {
            self.items[self.count] = value;
            self.count += 1;
        }

        pub fn pop(self: *@This()) ?T {
            if (self.count == 0) return null;
            self.count -= 1;
            return self.items[self.count];
        }
    };
}

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    var words = Stack([]const u8){};
    var empty = Stack(i32){};
    words.push("red");
    words.push("blue");
    if (words.pop()) |word| {
        try out.print("{s}\n", .{word});
    }
    try out.print("{?}\n", .{empty.pop()});
}

Example

const std = @import("std");

fn Stack(comptime T: type) type {
    return struct {
        items: [4]T = undefined,
        count: usize = 0,

        pub fn push(self: *@This(), value: T) void {
            self.items[self.count] = value;
            self.count += 1;
        }

        pub fn pop(self: *@This()) ?T {
            if (self.count == 0) return null;
            self.count -= 1;
            return self.items[self.count];
        }
    };
}

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    var nums = Stack(i32){};
    var names = Stack([]const u8){};
    nums.push(4);
    nums.push(9);
    nums.push(6);
    names.push("ana");
    names.push("bo");
    try out.print("{d}\n", .{nums.count});
    try out.print("{?}\n", .{nums.pop()});
    try out.print("{?}\n", .{nums.pop()});
    if (names.pop()) |name| {
        try out.print("{s}\n", .{name});
    }
    try out.print("{d}\n", .{names.count});
}

Key points

  • Generic struct: a struct whose field types come from a type argument.
  • Type factory: a function that takes comptime T: type and returns a type.
  • Pair(T): the returned struct holds two fields of type T.
  • Stack(T): the returned struct holds an array of T and a count.
  • push method: push stores a value and raises the count by one.
  • pop method: pop lowers the count and returns the stored value as an optional.

In this example, the Stack factory builds one stack of i32 values and one stack of words from the same code. Three pushes leave the number stack with a count of 3. Two pops then return 6 and 9, the last values pushed. The word stack pops bo, and its count falls to 1.

Note

Stack(i32) and Stack(u8) are two different types, even though they come from one factory.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

What does pop return when a stack built by Stack(i32) holds no values?