# comptime Basics — Zig

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

> Run code at compile time and use compile-time parameters.

## One language for run time and compile time

Zig's signature feature is **`comptime`**: ordinary Zig code can run **during compilation**. A `comptime` block or expression is evaluated by the compiler, and its result is embedded in the program: lookup tables, precomputed constants or validated configuration cost nothing at run time. Function parameters marked **`comptime`** must be known at compile time, which enables **specialisation**: the function is instantiated separately for each distinct value. **Types are values** at compile time (of type `type`), so they can be passed to functions, stored in constants and returned, which is how Zig implements **generics** without a separate template language. **`@compileError`** reports custom compile errors, perfect for validating assumptions ("this struct must be 64 bytes"). **`inline for`** and **`inline while`** unroll loops at compile time, for example over a struct's fields. There are limits: comptime code cannot perform I/O or call external functions, and long comptime evaluations need `@setEvalBranchQuota`. This replaces C's preprocessor macros, C++ templates and many code generators with one consistent language.

## Compile time and run time

comptime code runs in the compiler; its results become constants in the binary.

![A compiler box with a gear running a small function and producing a table, which is then baked into a binary file shown on the right.](assets/figures/zig/section-5-map.svg) — Figure 5.1 — Computation moved to compile time.

## Lookup tables and assertions at compile time

A table computed by the compiler and a size check.

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

fn fib(n: u64) u64 {
    return if (n < 2) n else fib(n - 1) + fib(n - 2);
}

// Computed entirely by the compiler; the binary just contains the array
const fib_table = blk: {
    @setEvalBranchQuota(100_000);                 // naive recursion needs many comptime steps
    var table: [20]u64 = undefined;
    for (&table, 0..) |*slot, i| slot.* = fib(i);
    break :blk table;
};

const Header = extern struct {
    magic: u32,
    version: u16,
    flags: u16,
    length: u64,
};

comptime {
    if (@sizeOf(Header) != 16) @compileError("Header must be exactly 16 bytes for the file format");
}

fn repeat(comptime n: usize, ch: u8) [n]u8 {          // n is part of the return type
    var out: [n]u8 = undefined;
    @memset(&out, ch);
    return out;
}

pub fn main() void {
    std.debug.print("fib(19) = {d}\n", .{fib_table[19]});   // 4181, no run-time computation
    const line = repeat(10, '=');
    std.debug.print("{s}\n", .{&line});
    std.debug.print("Header is {d} bytes\n", .{@sizeOf(Header)});
}
```

## Compile-time checks are documentation

A `comptime` assertion about a struct size, an enum count or a configuration value documents an assumption and turns its violation into a build failure instead of a production bug.

**Quiz:** What can a function do with a `comptime T: type` parameter?

- [x] Use T as a type in its body, making the function generic over T
- [ ] Nothing; types cannot be parameters
- [ ] Change T at run time
- [ ] Only print T

*Answer:* Use T as a type in its body, making the function generic over T. Types are compile-time values, which is how Zig does generics.
