Что такое Zig и зачем он нужен

Знакомимся с Zig — системным языком, который претендует на роль «лучшего C».

Zig — компилируемый системный язык программирования общего назначения, спроектированный как современная замена C: без скрытых аллокаций памяти, без скрытого потока управления и с выполнением кода на этапе компиляции вместо макросов.

Си правит системным программированием почти полвека. На нём написаны ядра операционных систем, компиляторы, базы данных и встраиваемые прошивки. Но у C есть болезни, с которыми программисты борются десятилетиями: разыменование NULL, переполнение целых чисел, неопределённое поведение, утечки памяти, висячие указатели. Современные языки вроде Rust решают эти проблемы, но платят за это сложностью — borrow checker требует переучиваться мыслить. Zig выбирает другой путь.

Идея Zig в одном предложении

Андрей Келли начал разрабатывать Zig в 2015 году с простой целью: взять C, убрать его худшие ловушки, но не превращать язык в монстра. Zig остаётся маленьким и прямолинейным. В нём нет препроцессора, нет перегрузки операторов, нет скрытых вызовов конструкторов и деструкторов, нет сборщика мусора. То, что вы видите в коде, — это ровно то, что выполнится.

Три принципа явности

Философия Zig держится на трёх «нет», которые отличают его от C++ и Rust.

Нет скрытым аллокациям памяти

В C++ строка std::vector<int> v; v.push_back(5); может незаметно выделить кучу памяти. В Zig любая функция, которая выделяет память, обязана принимать аллокатор явным параметром. Если функция не получила аллокатор — она физически не может тронуть кучу. Это значит, что во встраиваемой системе без динамической памяти вы сразу видите, какой код нарушает правила.

Нет скрытому потоку управления

В Zig нет исключений, которые незаметно раскручивают стек, нет перегрузки операторов, которая прячет вызов функции за знаком +, нет макросов, которые подменяют код. Если вы видите вызов функции — это вызов именно этой функции. Поток управления читается глазами без сюрпризов.

Нет препроцессору и макросам

Вместо макросов C и шаблонов C++ Zig предлагает comptime — обычный код Zig, который выполняется во время компиляции. Это и метапрограммирование, и дженерики, и кодогенерация на одном понятном языке.

const std = @import("std");

pub fn main() void {
    std.debug.print("Zig: явность важнее магии\n", .{});
}

Вывод:

Zig: явность важнее магии

Как работает под капотом

Zig компилируется в нативный машинный код через LLVM (а в новых версиях — через собственный бэкенд). Получается обычный бинарник без рантайма и без зависимостей, как у C. При этом компилятор Zig умеет ещё и собирать код на чистом C — он включает в себя полноценный кросс-компилятор C. Поэтому Zig нередко используют просто как удобную замену gcc или clang для кросс-сборки.

Частые ошибки

Главное заблуждение новичка из мира C++ — ждать, что стандартная библиотека сама выделит память «как обычно». В Zig почти каждый контейнер требует, чтобы вы передали ему аллокатор. Второе заблуждение — искать макросы и #ifdef. Их нет: условную компиляцию делают через comptime и обычный if.

Итог

  • Zig — системный язык, спроектированный как современная замена C.
  • Три кита: нет скрытых аллокаций, нет скрытого потока управления, нет макросов.
  • comptime заменяет и шаблоны, и препроцессор.
  • Компилятор Zig умеет собирать ещё и C, работая как кросс-компилятор.
Проверьте себя
1. Какой механизм Zig использует вместо макросов препроцессора C?
AШаблоны как в C++
Bcomptime — выполнение обычного кода на этапе компиляции
CСборщик мусора
DАннотации времени выполнения
2. Что означает принцип «нет скрытым аллокациям» в Zig?
AПамять нельзя выделять вообще
BФункция, выделяющая память, обязана явно принимать аллокатор
CАллокации делает сборщик мусора
DПамять выделяется только на стеке