# Generating Declarations

### Generating Declarations

A Zig function can return a type.

That type can contain fields, constants, and functions.

This is how Zig writes many generic containers.

```zig
fn Box(comptime T: type) type {
    return struct {
        value: T,
    };
}
```

Use it like this:

```zig
const IntBox = Box(i32);

var b = IntBox{
    .value = 123,
};
```

`Box(i32)` returns a struct type. The returned type has one field:

```zig
value: i32
```

Calling the same function with another type gives another struct type.

```zig
const BoolBox = Box(bool);
```

Now the field is:

```zig
value: bool
```

This is declaration generation. The declarations are not written by a preprocessor. They are produced by ordinary Zig code during compilation.

A returned struct may contain methods.

```zig
fn Counter(comptime T: type) type {
    return struct {
        value: T,

        fn init() Counter(T) {
            return .{ .value = 0 };
        }

        fn inc(self: *Counter(T)) void {
            self.value += 1;
        }
    };
}
```

Use it:

```zig
const U32Counter = Counter(u32);

var c = U32Counter.init();
c.inc();
```

The function `Counter` returns a type. That type contains the field `value` and the functions `init` and `inc`.

Inside the returned struct, the type can refer to itself by calling the generator again:

```zig
Counter(T)
```

A common idiom is to name the returned struct with `Self`.

```zig
fn Counter(comptime T: type) type {
    return struct {
        const Self = @This();

        value: T,

        fn init() Self {
            return .{ .value = 0 };
        }

        fn inc(self: *Self) void {
            self.value += 1;
        }
    };
}
```

`@This()` means the type currently being declared.

This makes the code easier to read and avoids repeating `Counter(T)`.

A generated type can also contain compile-time constants.

```zig
fn Buffer(comptime T: type, comptime N: usize) type {
    return struct {
        const Self = @This();
        const capacity = N;

        data: [N]T,
        len: usize,

        fn init() Self {
            return .{
                .data = undefined,
                .len = 0,
            };
        }
    };
}
```

Now each generated buffer type carries its capacity as a declaration.

```zig
const B = Buffer(u8, 128);

std.debug.print("{d}\n", .{B.capacity});
```

The value printed is:

```text
128
```

The constant belongs to the type, not to a particular value.

Generated declarations are useful when the type needs to remember compile-time parameters.

Here is a small stack.

```zig
fn Stack(comptime T: type, comptime N: usize) type {
    return struct {
        const Self = @This();

        data: [N]T,
        len: usize,

        fn init() Self {
            return .{
                .data = undefined,
                .len = 0,
            };
        }

        fn push(self: *Self, value: T) !void {
            if (self.len == N)
                return error.Full;

            self.data[self.len] = value;
            self.len += 1;
        }

        fn pop(self: *Self) ?T {
            if (self.len == 0)
                return null;

            self.len -= 1;
            return self.data[self.len];
        }
    };
}
```

Use it:

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

pub fn main() !void {
    var s = Stack(i32, 4).init();

    try s.push(10);
    try s.push(20);

    std.debug.print("{?d}\n", .{s.pop()});
    std.debug.print("{?d}\n", .{s.pop()});
    std.debug.print("{?d}\n", .{s.pop()});
}
```

The output is:

```text
20
10
null
```

The stack has no heap allocation. Its size is part of its type.

```zig
Stack(i32, 4)
```

and

```zig
Stack(i32, 8)
```

are different types.

The compiler can check their sizes and operations separately.

Generated declarations may also select implementations.

```zig
fn Storage(comptime T: type, comptime small: bool) type {
    return if (small)
        struct {
            value: T,
        }
    else
        struct {
            ptr: *T,
        };
}
```

The returned type depends on a compile-time value.

This is not runtime branching. The compiler chooses one type while compiling.

Use declaration generation when the shape of a type depends on compile-time arguments.

Do not use it where a plain struct is enough.

This is enough for ordinary data:

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

A generator is useful only when the declarations themselves must vary.

```zig
const Point3 = Vector(f64, 3);
const Point4 = Vector(f64, 4);
```

Zig keeps this mechanism simple. A function receives compile-time values and returns a type. The returned type contains ordinary declarations.

Exercise 10-21. Write `Box(comptime T: type) type`.

Exercise 10-22. Add `get` and `set` methods to `Box`.

Exercise 10-23. Write `Stack(comptime T: type, comptime N: usize) type`.

Exercise 10-24. Add a `capacity` declaration to `Stack`.

