# Inline Loops

### Inline Loops

Zig has normal loops that run when the program runs.

```zig
for (items) |item| {
    process(item);
}
```

Zig also has inline loops.

An inline loop is expanded by the compiler.

That means the compiler does not treat it as an ordinary runtime loop. Instead, it repeats the loop body during compilation and generates code from the result.

The two forms are:

```zig
inline for (items) |item| {
    // repeated at compile time
}
```

and:

```zig
inline while (condition) : (update) {
    // repeated at compile time
}
```

For beginners, the most important one is `inline for`.

#### A Normal `for` Loop

Start with a normal loop:

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

pub fn main() void {
    const numbers = [_]u8{ 1, 2, 3 };

    for (numbers) |n| {
        std.debug.print("{}\n", .{n});
    }
}
```

This prints:

```text
1
2
3
```

This loop runs at runtime.

When the program runs, it walks through the array and prints each item.

#### An `inline for` Loop

Now compare this:

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

pub fn main() void {
    const numbers = [_]u8{ 1, 2, 3 };

    inline for (numbers) |n| {
        std.debug.print("{}\n", .{n});
    }
}
```

It prints the same output:

```text
1
2
3
```

But the meaning is different.

The compiler expands the loop. Conceptually, it becomes something like:

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

This is not always the exact generated code, but it is the right mental model.

An `inline for` says: repeat this code at compile time for each item.

#### Why Inline Loops Exist

Inline loops are useful when the loop controls code generation.

That usually means one of these cases:

You are looping over types.

You are looping over fields of a struct.

You are generating repeated code at compile time.

You need each iteration to be known separately by the compiler.

A normal runtime loop is for data.

An inline loop is often for code structure.

#### Looping Over Types

A normal runtime loop cannot loop over types.

Types exist at compile time.

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

pub fn main() void {
    const types = .{ u8, u16, u32 };

    inline for (types) |T| {
        std.debug.print("{} has size {}\n", .{ T, @sizeOf(T) });
    }
}
```

This prints something like:

```text
u8 has size 1
u16 has size 2
u32 has size 4
```

The tuple:

```zig
.{ u8, u16, u32 }
```

contains types.

Each iteration gives a type named `T`.

Then this expression:

```zig
@sizeOf(T)
```

asks the compiler for the size of that type.

A normal `for` loop cannot do this, because types are not runtime values.

#### Inline Loops and Tuples

Inline loops are common with tuples.

A tuple can hold values of different types:

```zig
const values = .{ 123, true, "zig" };
```

The first item is an integer.

The second item is a boolean.

The third item is a string.

A normal `for` loop expects items with a consistent runtime shape. An inline loop can handle each tuple field separately:

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

pub fn main() void {
    const values = .{ 123, true, "zig" };

    inline for (values) |value| {
        std.debug.print("{}\n", .{value});
    }
}
```

Each iteration is compiled separately, so Zig can type-check each value with its own type.

#### Inline Loops and Struct Fields

One powerful use of `inline for` is reflection.

Reflection means inspecting program structure, such as the fields of a type.

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

const User = struct {
    id: u32,
    name: []const u8,
    active: bool,
};

pub fn main() void {
    inline for (@typeInfo(User).@"struct".fields) |field| {
        std.debug.print("{s}\n", .{field.name});
    }
}
```

This prints:

```text
id
name
active
```

This line asks Zig for information about `User`:

```zig
@typeInfo(User)
```

The struct information contains its fields.

The inline loop walks through those fields at compile time.

This is the kind of code that makes Zig’s compile-time system useful. You can write code that understands types without using a separate macro language.

#### Runtime Loop vs Inline Loop

A runtime loop repeats work while the program runs.

```zig
for (items) |item| {
    use(item);
}
```

An inline loop repeats code while the program is being compiled.

```zig
inline for (items) |item| {
    use(item);
}
```

The difference matters.

A runtime loop is good for a list of values loaded from a file, read from the network, or built while the program runs.

An inline loop requires compile-time-known input. The compiler must know what it is expanding.

This works:

```zig
const values = .{ 1, 2, 3 };

inline for (values) |x| {
    _ = x;
}
```

This does not fit the purpose of `inline for`:

```zig
var values = [_]u8{ 1, 2, 3 };

inline for (values) |x| {
    _ = x;
}
```

If the loop input is ordinary runtime data, use a normal `for`.

#### Inline Loops Can Reduce Abstraction Cost

Suppose you want to run the same test for several integer types.

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

fn maxValue(comptime T: type) T {
    return std.math.maxInt(T);
}

test "max values" {
    const types = .{ u8, u16, u32 };

    inline for (types) |T| {
        try std.testing.expect(maxValue(T) > 0);
    }
}
```

The compiler expands the loop into separate checks.

Conceptually:

```zig
try std.testing.expect(maxValue(u8) > 0);
try std.testing.expect(maxValue(u16) > 0);
try std.testing.expect(maxValue(u32) > 0);
```

Each call is specialized for its type.

This gives you generic-looking code without hiding what the compiler must generate.

#### Inline Loops Are Not for Everything

Do not use `inline for` just because it looks faster.

This:

```zig
for (items) |item| {
    process(item);
}
```

is usually what you want for ordinary data.

This:

```zig
inline for (items) |item| {
    process(item);
}
```

asks the compiler to duplicate the loop body for each item. That can increase code size.

Inline loops are best when the compiler needs each iteration to be separate.

Good uses:

Looping over types.

Looping over tuple fields.

Looping over struct fields.

Generating code from compile-time-known data.

Avoid it for ordinary arrays that should be processed at runtime.

#### `inline while`

Zig also supports `inline while`.

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

pub fn main() void {
    comptime var i = 0;

    inline while (i < 3) : (i += 1) {
        std.debug.print("i = {}\n", .{i});
    }
}
```

This prints:

```text
i = 0
i = 1
i = 2
```

The variable `i` is a compile-time variable:

```zig
comptime var i = 0;
```

The loop is expanded during compilation.

For beginners, `inline while` is less common than `inline for`. You will mostly see it in advanced compile-time code.

#### A Practical Example: Generate a Parser Table

Imagine you have a fixed set of keywords:

```zig
const keywords = .{
    "if",
    "else",
    "while",
    "return",
};
```

You can use `inline for` to generate checks:

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

fn isKeyword(text: []const u8) bool {
    const keywords = .{
        "if",
        "else",
        "while",
        "return",
    };

    inline for (keywords) |keyword| {
        if (std.mem.eql(u8, text, keyword)) {
            return true;
        }
    }

    return false;
}

pub fn main() void {
    std.debug.print("{}\n", .{isKeyword("while")});
}
```

This prints:

```text
true
```

The keyword list is known at compile time.

The compiler can expand the checks.

For a small fixed list, this is simple and clear. For a large list, you may want a different structure such as a hash table. Inline code generation should still be used with judgment.

#### The Main Idea

An inline loop is a compile-time loop.

A normal loop repeats work at runtime.

An inline loop repeats code during compilation.

Use normal loops for ordinary data:

```zig
for (items) |item| {
    process(item);
}
```

Use inline loops when the loop is part of compile-time code generation:

```zig
inline for (types) |T| {
    useType(T);
}
```

The beginner rule is simple: do not reach for `inline` first. Write a normal loop unless the compiler needs each iteration to be known separately.

