# Compile-Time Variables

### Compile-Time Variables

In the previous section, you learned that Zig can execute code during compilation.

Now we will look at compile-time variables.

A compile-time variable is a variable that exists while the compiler is building the program. Its value is known completely before runtime begins.

This is different from normal runtime variables.

#### Runtime Variables

Here is a normal runtime variable:

```zig
pub fn main() void {
    var x: i32 = 10;
    x += 5;
}
```

The variable `x` exists while the program runs.

The CPU creates storage for it at runtime. The value may change during execution.

#### Compile-Time Variables

Now look at this:

```zig
comptime {
    const x = 10;
    const y = x + 5;
}
```

This block runs during compilation.

The variables `x` and `y` exist only inside the compiler while it is building the program.

They do not exist in the final executable.

#### The `comptime` Block

A `comptime` block forces code to execute during compilation.

Example:

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

comptime {
    std.debug.print("hello\n", .{});
}
```

When the compiler processes this file, the block executes immediately.

The output appears during compilation, not when the final program runs.

This is an important distinction.

Runtime code runs after you start the executable.

Compile-time code runs while Zig is still generating the executable.

#### Compile-Time Constants

Many constants are naturally compile-time values.

```zig
const a = 100;
const b = a * 2;
```

Because these values are fully known, Zig can compute them during compilation.

You often do not need to write `comptime` explicitly. Zig automatically recognizes many compile-time expressions.

#### Compile-Time Expressions

An expression is compile-time when all required information is already known.

Example:

```zig
const size = 4 * 8;
```

The compiler already knows both numbers, so it computes the result immediately.

But this is different:

```zig
var runtime_value: usize = 4;
const size = runtime_value * 8;
```

Here, `runtime_value` is a runtime variable.

Its value is not guaranteed to be known during compilation.

Therefore `size` is not a compile-time expression.

#### Array Lengths Must Be Compile-Time Known

One of the most common places where compile-time values are required is array lengths.

```zig
const numbers: [4]i32 = .{ 1, 2, 3, 4 };
```

The compiler must know the size of the array before the program runs.

This works:

```zig
const count = 4;

const numbers: [count]i32 = .{
    1,
    2,
    3,
    4,
};
```

Because `count` is compile-time known.

But this fails:

```zig
var count: usize = 4;

const numbers: [count]i32 = undefined;
```

The compiler cannot use a runtime variable as an array length.

#### Compile-Time Function Parameters

You already saw this pattern:

```zig
fn square(comptime n: i32) i32 {
    return n * n;
}
```

The parameter `n` must be known during compilation.

```zig
const result = square(5);
```

This works.

But this fails:

```zig
var x: i32 = 5;
const result = square(x);
```

Why?

Because `x` is a runtime variable.

Even though its value currently happens to be `5`, Zig treats it as runtime data because it may change later.

#### Compile-Time Loops

You can use loops during compilation too.

Example:

```zig
comptime {
    var sum: u32 = 0;

    for (0..5) |i| {
        sum += i;
    }
}
```

The loop executes inside the compiler.

No runtime code is generated for this loop.

#### Using `inline for`

`inline for` is closely connected to compile-time execution.

Example:

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

pub fn main() void {
    inline for (.{ 1, 2, 3 }) |value| {
        std.debug.print("{}\n", .{value});
    }
}
```

The compiler unrolls the loop.

Conceptually, Zig transforms this:

```zig
std.debug.print("{}\n", .{1});
std.debug.print("{}\n", .{2});
std.debug.print("{}\n", .{3});
```

This is useful for metaprogramming and specialization.

#### Compile-Time Mutable Variables

You can use mutable variables at compile time.

```zig
comptime {
    var total: i32 = 0;

    total += 10;
    total += 20;
}
```

The variable changes while the compiler executes the block.

But again, this variable exists only during compilation.

It disappears after compilation finishes.

#### Building Data at Compile Time

Compile-time execution is useful for generating data structures ahead of time.

Example:

```zig
const table = comptime blk: {
    var values: [4]u32 = undefined;

    for (0..4) |i| {
        values[i] = @intCast(i * i);
    }

    break :blk values;
};
```

This creates the array during compilation.

The resulting array becomes part of the final program.

The loop itself does not run at runtime.

#### Understanding `comptime` Blocks

A `comptime` block is an instruction to the compiler:

Execute this code now.

Example:

```zig
comptime {
    @compileLog("building program");
}
```

`@compileLog` prints information during compilation.

This is useful for debugging compile-time logic.

#### Compile-Time Types

Types themselves are compile-time values.

```zig
comptime {
    const T = i32;
}
```

The variable `T` stores a type.

This is completely normal in Zig.

You can compare types too:

```zig
fn printInfo(comptime T: type) void {
    if (T == i32) {
        @compileLog("signed integer");
    }
}
```

The compiler evaluates the condition while compiling.

#### Compile-Time Decisions

Compile-time variables allow Zig to specialize code.

Example:

```zig
fn maxValue(comptime T: type) T {
    return switch (T) {
        u8 => 255,
        u16 => 65535,
        else => @compileError("unsupported type"),
    };
}
```

The compiler selects the correct branch based on the compile-time type.

```zig
const a = maxValue(u8);
const b = maxValue(u16);
```

Each call becomes specialized code.

#### Runtime Variables Cannot Become Compile-Time Values

This is one of the most important rules in Zig.

Runtime data cannot travel backward into compilation.

Example:

```zig
fn getNumber() i32 {
    return 42;
}

pub fn main() void {
    const x = getNumber();
}
```

Even though `getNumber()` always returns `42`, Zig still treats this as runtime behavior unless the function itself is evaluated at compile time.

The compiler must be able to prove the value is known during compilation.

#### Compile-Time Memory

Compile-time variables exist only inside the compiler process.

They are not stored in the final executable unless their values are embedded into generated data.

For example:

```zig
const values = comptime blk: {
    var array: [3]u8 = undefined;

    array[0] = 10;
    array[1] = 20;
    array[2] = 30;

    break :blk array;
};
```

The compiler builds the array during compilation, then stores the final result inside the executable.

The temporary compile-time variables disappear afterward.

#### Compile-Time Safety

Compile-time execution is still checked by Zig’s safety rules.

Example:

```zig
comptime {
    const numbers = [_]u8{ 1, 2, 3 };
    const x = numbers[10];

    _ = x;
}
```

This fails during compilation because the array index is invalid.

The compiler catches the mistake immediately.

#### Why Compile-Time Variables Matter

Compile-time variables are one of the foundations of Zig’s design.

They allow Zig to:

| Feature | Benefit |
|---|---|
| Generic functions | Reuse code across types |
| Reflection | Inspect types during compilation |
| Code specialization | Generate optimized code |
| Static validation | Catch mistakes early |
| Build-time generation | Create tables and structures ahead of time |
| Zero-cost abstractions | Remove runtime overhead |

Without compile-time execution, Zig would need separate systems for templates, macros, and code generation.

Instead, Zig uses ordinary Zig code during compilation.

#### Mental Model

Think of Zig as having two execution worlds:

| World | When It Runs |
|---|---|
| Compile time | While the compiler builds the program |
| Runtime | After the executable starts |

Compile-time variables belong to the first world.

Runtime variables belong to the second world.

The compiler can move information forward:

compile time → runtime

But runtime information cannot move backward:

runtime → compile time

That rule explains many parts of Zig’s behavior.

