# Compile-Time Parameters

### Compile-Time Parameters

A function parameter can be marked `comptime`.

```zig
fn repeat(comptime n: usize, value: u8) [n]u8 {
    var out: [n]u8 = undefined;

    for (&out) |*item| {
        item.* = value;
    }

    return out;
}
```

Here `n` is not an ordinary parameter. It must be known while the program is being compiled.

This call is valid:

```zig
const a = repeat(4, 'x');
```

The compiler sees `n == 4`, so the return type becomes:

```zig
[4]u8
```

The array length is part of the type. The compiler cannot create `[n]u8` unless it knows `n`.

This call is not valid:

```zig
var len: usize = 4;
const b = repeat(len, 'x');
```

`len` is a runtime variable. Its value may change while the program runs. It cannot be used where the compiler needs a type.

A compile-time parameter lets a function receive information that shapes the program itself.

Types are the most common example.

```zig
fn zero(comptime T: type) T {
    return 0;
}
```

The call:

```zig
const a = zero(i32);
const b = zero(u64);
```

creates values of different types from the same function.

The first call returns `i32`. The second returns `u64`.

A generic function in Zig is just an ordinary function with compile-time parameters.

```zig
fn add(comptime T: type, a: T, b: T) T {
    return a + b;
}
```

Use it like this:

```zig
const x = add(i32, 10, 20);
const y = add(f64, 1.25, 2.75);
```

The compiler checks each use separately.

For `i32`, `+` means integer addition.

For `f64`, `+` means floating-point addition.

If a type does not support the operation, compilation fails.

```zig
const bad = add(bool, true, false);
```

This is rejected because `+` is not defined for `bool`.

There is no separate interface declaration here. The function body itself states what it needs from `T`.

If the function uses `+`, then `T` must support `+`.

This style keeps generic code close to ordinary code.

Compile-time parameters may also be booleans.

```zig
fn log(comptime enabled: bool, message: []const u8) void {
    if (enabled) {
        std.debug.print("{s}\n", .{message});
    }
}
```

When `enabled` is known at compile time, the compiler can remove the branch when it is false.

```zig
log(false, "not printed");
```

This call need not generate any printing code.

The choice was made before the program ran.

Compile-time parameters may also control data layout.

```zig
fn Buffer(comptime N: usize) type {
    return struct {
        data: [N]u8,
        len: usize,

        fn empty() Buffer(N) {
            return .{
                .data = undefined,
                .len = 0,
            };
        }
    };
}
```

This function returns a type.

```zig
const Small = Buffer(16);
const Large = Buffer(4096);
```

`Small` and `Large` are different types.

`Small` contains `[16]u8`.

`Large` contains `[4096]u8`.

The function `Buffer` is not a factory for runtime objects. It is a function that builds a type during compilation.

This is a common Zig pattern.

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

fn Buffer(comptime N: usize) type {
    return struct {
        data: [N]u8,
        len: usize,

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

        fn append(self: *Buffer(N), byte: u8) !void {
            if (self.len >= N)
                return error.Full;

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

pub fn main() !void {
    var b = Buffer(4).init();

    try b.append('a');
    try b.append('b');

    std.debug.print("{s}\n", .{b.data[0..b.len]});
}
```

The output is:

```text
ab
```

The size of the buffer is known at compile time. No heap allocation is needed.

Compile-time parameters should be used when they change types, layout, or generated code.

They should not be used for ordinary values merely because those values happen to be constants.

This is good:

```zig
fn Matrix(comptime rows: usize, comptime cols: usize) type {
    return struct {
        data: [rows][cols]f64,
    };
}
```

The dimensions are part of the type.

This is usually unnecessary:

```zig
fn printNumber(comptime n: i32) void {
    std.debug.print("{d}\n", .{n});
}
```

The number does not shape the program. A normal parameter is enough.

Compile-time parameters are part of the function's type-level interface. They tell the reader which values must be known before execution starts.

Exercise 10-5. Write a function `arrayOf` that takes `comptime n: usize` and a value, and returns an array of length `n`.

Exercise 10-6. Write a generic function `min` with a `comptime T: type` parameter.

Exercise 10-7. Write a function `Pair(comptime T: type) type` that returns a struct with fields `first: T` and `second: T`.

Exercise 10-8. Write `Stack(comptime T: type, comptime N: usize) type` using an array of length `N`.

