Метапрограммирование без макросов
Используем comptime для интроспекции типов и генерации кода без единого макроса.
Интроспекция типов — способность кода во время компиляции исследовать структуру типа (его поля, варианты, размер) и генерировать код в зависимости от этого; в Zig её даёт встроенная функция
@typeInfo.
comptime — это не только дженерики. Поскольку при компиляции доступны типы как значения, можно исследовать их структуру и порождать код на лету. Это заменяет и макросы C, и кодогенераторы, и рефлексию — всё на одном языке.
Интроспекция через @typeInfo
const std = @import("std");
const Point = struct { x: i32, y: i32 };
pub fn main() void {
const info = @typeInfo(Point);
// перебираем поля структуры на этапе компиляции
inline for (info.@"struct".fields) |field| {
std.debug.print("поле: {s}\n", .{field.name});
}
}
Вывод:
поле: x поле: y
Функция @typeInfo возвращает структуру с описанием типа: для struct — список полей с именами и типами. Цикл inline for разворачивается при компиляции и проходит по полям. Так можно автоматически сгенерировать сериализацию, валидацию или вывод — без ручного перечисления полей.
inline for — развёртка при компиляции
Обычный for работает в рантайме. inline for же разворачивается компилятором: тело копируется для каждого элемента, и это позволяет внутри использовать значения, известные только при компиляции (например, имя поля как тип). Это мост между миром типов и обычным кодом.
Условная компиляция без #ifdef
const builtin = @import("builtin");
fn logIfDebug(msg: []const u8) void {
// ветка выбирается на этапе компиляции по режиму сборки
if (builtin.mode == .Debug) {
std.debug.print("[debug] {s}\n", .{msg});
}
// в release ветка просто исчезает из бинарника
}
В C условную компиляцию делают через #ifdef. В Zig — обычным if по comptime-значению. Если условие известно при компиляции (как режим сборки), компилятор полностью убирает мёртвую ветку. Никакого препроцессора, тот же синтаксис, что и в рантайме.
Как работает под капотом
@typeInfo и подобные встроенные функции (@hasField, @field, @TypeOf) дают компилятору доступ к его внутреннему представлению типов в виде обычных Zig-значений. Поскольку всё это comptime-данные, генерация кода — это просто исполнение функций при компиляции с подстановкой результата. Отсюда и сила, и безопасность: метапрограммы пишутся и проверяются как обычный типизированный код, а не как текстовые макросы.
Частые ошибки
Первая — использовать обычный for там, где нужен inline for для перебора comptime-сущностей. Вторая — ждать рефлексии в рантайме: вся интроспекция Zig — на этапе компиляции, в бинарнике её следов нет. Третья — переусложнять comptime-логику: мощь соблазняет, но читаемость страдает, если перегнуть.
Итог
@typeInfoдаёт структуру типа (поля, варианты) как comptime-значение.inline forразворачивается при компиляции и связывает мир типов с обычным кодом.- Условную компиляцию делают обычным
ifпо comptime-значению, без#ifdef. - Метапрограммы Zig — типизированный код при компиляции, а не текстовые макросы.