# Generic Types and Functions — Zig

Source: https://www.geekswithgeeks.com/en/zig/k-generics

> Write generic data structures with functions that return types.

## Functions that return types

A **generic function** takes a `comptime T: type` parameter, or uses **`anytype`** to infer the parameter's type at each call: `fn max(comptime T: type, a: T, b: T) T`. A **generic data structure** is a **function that returns a type**: `fn Stack(comptime T: type) type { return struct { ... }; }`, used as `Stack(u32)`. Inside, **`@This()`** names the returned struct, conventionally as `const Self = @This();`. The standard library uses this everywhere: `std.ArrayList(T)`, `std.AutoHashMap(K, V)`, `std.PriorityQueue(T, Context, compareFn)`. Because generics are just compile-time function calls, you can add **constraints and errors** in plain code: check `@typeInfo(T)` and call `@compileError` with a helpful message if a type is unsupported. Instantiations with the same arguments are memoised, so `Stack(u32)` is always the same type. Zig has no interfaces or traits keyword; runtime polymorphism uses explicit **vtables** (a struct of function pointers plus a context pointer), as `std.mem.Allocator` and `std.Io.Writer` do, or tagged unions when the set of implementations is closed.

## A generic bounded stack

A function returning a struct type, with comptime capacity.

```zig
const std = @import("std");

fn BoundedStack(comptime T: type, comptime capacity: usize) type {
    return struct {
        items: [capacity]T = undefined,
        len: usize = 0,

        const Self = @This();

        pub fn push(self: *Self, value: T) error{Full}!void {
            if (self.len == capacity) return error.Full;
            self.items[self.len] = value;
            self.len += 1;
        }

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

        pub fn peek(self: *const Self) ?T {
            return if (self.len == 0) null else self.items[self.len - 1];
        }
    };
}

fn largest(comptime T: type, values: []const T) ?T {
    if (values.len == 0) return null;
    var best = values[0];
    for (values[1..]) |v| best = @max(best, v);
    return best;
}

test "bounded stack" {
    var s: BoundedStack(u32, 2) = .{};
    try s.push(10);
    try s.push(20);
    try std.testing.expectError(error.Full, s.push(30));
    try std.testing.expectEqual(@as(?u32, 20), s.pop());
    try std.testing.expectEqual(@as(?u32, 10), s.peek());
}

test "largest" {
    try std.testing.expectEqual(@as(?i32, 9), largest(i32, &.{ 3, 9, -2 }));
    try std.testing.expectEqual(@as(?f64, null), largest(f64, &.{}));
}
```

## A mould factory

BoundedStack is not a stack; it is a factory that makes stack moulds. Ask for a mould for u32 with room for 2, and the compiler casts it once; every later request for the same mould gets the same one.

**Quiz:** How are generic data structures usually written in Zig?

- [ ] With a template keyword
- [ ] With macros
- [ ] With inheritance
- [x] As functions that take comptime parameters and return a type

*Answer:* As functions that take comptime parameters and return a type. For example, fn Stack(comptime T: type) type { return struct { ... }; }.
