comptime: код на этапе компиляции
Открываем главную суперсилу Zig — выполнение обычного кода во время компиляции.
comptime — ключевое слово, помечающее код, который выполняется на этапе компиляции, а не в рантайме; в Zig это единый механизм для констант, дженериков и метапрограммирования.
Если у Zig есть одна определяющая черта — это comptime. Идея проста, но мощна: тот же самый код Zig, который вы пишете для рантайма, может выполняться и во время компиляции. Не отдельный язык макросов, не шаблоны со своим синтаксисом — обычный Zig, исполненный компилятором.
Вычисления на этапе компиляции
fn factorial(n: u64) u64 {
var result: u64 = 1;
var i: u64 = 2;
while (i <= n) : (i += 1) result *= i;
return result;
}
// comptime заставляет вычислить значение при компиляции
const fact10 = comptime factorial(10); // 3628800, посчитано компилятором
Ключевое слово comptime перед выражением требует вычислить его при компиляции. factorial(10) исполнится компилятором, и в бинарник попадёт уже готовое число 3628800 — в рантайме ничего не считается. При этом factorial — обычная функция, её же можно вызвать и в рантайме.
Типы — это значения
// тип можно положить в const, как обычное значение
const MyInt = i32;
const x: MyInt = 5;
// и даже выбрать тип на этапе компиляции
const T = if (true) i32 else f64; // T == i32
Здесь скрыта революционная идея: в Zig типы — это значения типа type, доступные на этапе компиляции. Их можно присваивать константам, передавать в функции, выбирать через if. Именно это превращает обычные функции в генераторы дженериков, о чём — следующий урок.
comptime-параметры функций
fn repeat(comptime n: usize, value: u8) [n]u8 {
// n известно при компиляции, поэтому можно вернуть массив [n]u8
return [_]u8{value} ** n;
}
const trio = repeat(3, 65); // [3]u8{ 65, 65, 65 }
Параметр, помеченный comptime, обязан быть известен при компиляции. Это позволяет использовать его там, где нужны константы: например, как длину возвращаемого массива. Оператор ** повторяет массив n раз — тоже comptime-операция.
Как работает под капотом
Компилятор Zig содержит интерпретатор подмножества языка. Когда он встречает comptime-вычисление, он исполняет код прямо во время компиляции и подставляет результат. Поскольку это тот же язык, нет разрыва между «языком макросов» и «языком программы» — отсюда читаемость. Ограничение: comptime-код не может делать то, что требует рантайма (например, обращаться к вводу-выводу).
Частые ошибки
Первая — ждать, что comptime выполнит ввод-вывод или системные вызовы: ему доступны только чистые вычисления. Вторая — забыть пометить параметр comptime, когда от него зависит тип возврата, и получить ошибку «значение неизвестно при компиляции». Третья — путать comptime с препроцессором: это полноценный Zig, а не текстовая подстановка.
Итог
comptimeисполняет обычный код Zig на этапе компиляции — без отдельного языка макросов.- В Zig типы — это значения типа
type, доступные при компиляции. - Параметр
comptime nможно использовать там, где нужны константы (например, длина массива). - comptime-код чист: ввод-вывод и системные вызовы ему недоступны.