# Inline Loops

### Inline Loops

A normal loop runs while the program runs.

An inline loop is expanded while the program is compiled.

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

This is close to writing:

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

The loop itself does not exist at runtime in the same way. The compiler uses the loop to generate repeated code.

Inline loops are most useful when each iteration must be known at compile time.

A common case is iterating over a tuple.

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

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

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

The tuple contains values of different types.

A normal `for` loop cannot handle this as one runtime sequence, because each element has a different type.

The inline loop works because the compiler expands each iteration separately.

The generated code is like:

```zig
std.debug.print("{any}\n", .{10});
std.debug.print("{any}\n", .{true});
std.debug.print("{any}\n", .{"zig"});
```

Each call is checked with the exact type of its value.

Inline loops are also useful with types.

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

pub fn main() void {
    inline for (.{ i8, i16, i32, i64 }) |T| {
        std.debug.print("{s}: {d} bytes\n", .{
            @typeName(T),
            @sizeOf(T),
        });
    }
}
```

Typical output:

```text
i8: 1 bytes
i16: 2 bytes
i32: 4 bytes
i64: 8 bytes
```

The loop variable `T` is a type value. It exists at compile time.

A normal loop cannot range over types. Types are not runtime objects.

Inline loops can be used inside generic code.

```zig
fn hasField(comptime T: type, comptime name: []const u8) bool {
    inline for (@typeInfo(T).@"struct".fields) |field| {
        if (std.mem.eql(u8, field.name, name))
            return true;
    }

    return false;
}
```

This walks the fields of a struct while compiling.

For example:

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

comptime {
    if (!hasField(Point, "x"))
        @compileError("Point must have field x");
}
```

The check happens before the program can run.

Inline loops can also build repeated tests.

```zig
test "integer sizes" {
    inline for (.{ u8, u16, u32, u64 }) |T| {
        try std.testing.expect(@sizeOf(T) * 8 == @typeInfo(T).int.bits);
    }
}
```

Each iteration is its own compile-time expansion.

Use `inline for` when the loop body depends on compile-time information.

Use an ordinary `for` when the loop is merely processing runtime data.

This is ordinary runtime code:

```zig
for (items) |item| {
    sum += item;
}
```

This is compile-time code generation:

```zig
inline for (fields) |field| {
    // generate field-specific code
}
```

Inline loops should be used carefully. They duplicate code. If the sequence is large, the generated program may become larger.

The rule is simple: use an inline loop when the compiler must see each iteration separately.

Exercise 10-13. Use `inline for` to print the names and sizes of `u8`, `u16`, `u32`, and `u64`.

Exercise 10-14. Write a function that checks whether a struct has a field with a given name.

Exercise 10-15. Use `inline for` over a tuple containing values of different types.

Exercise 10-16. Rewrite a normal loop as an inline loop and compare the two cases.

