Почему Zig, когда уже есть C++, D и Rust?
Нет скрытых потоков управления
Если код на Zig не выглядит так, будто он вызывает функцию, значит, он её не вызывает. Можно быть уверенным, что в приведённом ниже коде вызываются только foo()
, а затем bar()
– независимо от типов данных:
var a = b + c.d;
foo();
bar();
Примеры скрытых потоков управления:
- В языке D есть
@property
-функции, которые вызываются как доступ к полю. Например, в D выражениеc.d
может быть вызовом функции. - C++, D и Rust поддерживают перегрузку операторов, в результате чего оператор
+
может вызывать функции. - В языках C++, D и Go есть механизм исключений с использованием throw/catch, из-за чего
foo()
может выбросить исключение и помешать вызовуbar()
. (Конечно, даже в Zig вызовfoo()
может привести к взаимной блокировке и помешатьbar()
выполниться, но подобное возможно в любом Тьюринг-полном языке.)
Zig делает код более читабельным и простым, управляя потоком выполнения исключительно при помощи ключевых слов и вызовов функций.
Нет скрытых выделений памяти
Zig позволяет разработчикам полностью управлять выделением памяти в куче. В языке отсутствует ключевое слово new
или другие механизмы, которые бы автоматически использовали кучу (например, оператор объединения строк[1]). Концепция работы с кучей полностью отдана на откуп библиотекам и собственно приложению, а не встроена в сам язык.
Примеры скрытого выделения памяти:
- В Go использование
defer
приводит к выделению памяти на локальном стеке функции. Этот подход к управлению потоком может быть неочевидным и способен вызвать ошибки из-за нехватки памяти, особенно еслиdefer
используется внутри цикла.* В C++ корутины используют память в куче для выполнения своих вызовов. - В Go вызов функции может вызвать выделение памяти в куче, поскольку горутины используют небольшие стеки, которые увеличиваются по мере углубления стека вызовов.
- Основные API стандартной библиотеки Rust паникуют при нехватке памяти, а альтернативные API, принимающие параметры аллокатора, недостаточно проработаны (см. rust-lang/rust#29802).
Почти все языки программирования с автоматическим управлением памятью имеют скрытые выделения памяти, так как сборщик мусора незаметно управляет ими при очистке, скрывая их от глаз разработчика.
Основная проблема скрытых выделений памяти в том, что они затрудняют переиспользование кода, неоправданно ограничивая количество сред, в которых этот код можно использовать. Проще говоря, существуют случаи, когда важно быть уверенным, что поток управления и вызовы функций не будут иметь побочных эффектов в виде выделения памяти, и язык программирования может удовлетворять этим требованиям только в том случае, если он чётко регулирует, когда и где выделяется память.
В стандартной библиотеке Zig существуют функции, которые позволяют работать с аллокаторами памяти в куче. Однако эти функции не являются обязательными и не встроены в сам язык. Если вы не инициализируете такой аллокатор, можете быть уверены, что ваша программа не будет выделять память в куче.
Каждая функция стандартной библиотеки Zig, которая требует выделения памяти в куче, принимает параметр Allocator
. Это означает, что стандартная библиотека Zig поддерживает и независимые целевые платформы. Так, например, std.ArrayList
и std.AutoHashMap
могут использоваться для программирования на голом железе!
Кастомные аллокаторы упрощают ручное управление памятью. В Zig есть отладочный аллокатор, который обеспечивает безопасность памяти, предотвращая ошибки, такие как использование освобожденной памяти или двойное освобождение. Он автоматически обнаруживает и выводит стек вызовов для утечек памяти. Также существует арена-аллокатор, который позволяет объединять любое количество аллокаций в одну, освобождая их все сразу, вместо того чтобы управлять каждой аллокацией отдельно. Специальные аллокаторы могут быть использованы для повышения производительности или оптимизации использования памяти в зависимости от потребностей конкретного приложения.
[1]: На самом деле в Zig есть оператор объединения строк (или, более широко, объединения массивов), но он работает только на этапе компиляции и, следовательно, не требует выделения памяти в куче во время выполнения программы.
Полноценная поддержка работы без стандартной библиотеки
Как уже упоминалось, стандартная библиотека в Zig является полностью опциональной. Каждый API стандартной библиотеки компилируется в вашу программу только при его использовании. Zig одинаково поддерживает как связывание с libc, так и работу без него. Этот язык удобен для разработок как на голом железе, так и для создания сложных высокопроизводительных приложений.
Таким образом, вы получаете лучшее из обоих миров: например, программы WebAssembly на Zig могут использовать стандартную библиотеку и при этом создавать минимальные по размеру бинарные файлы по сравнению с другими языками, поддерживающими компиляцию в WebAssembly.
Кроссплатформенный язык для библиотек
Переиспользование кода является одной из ключевых идей в программировании. Однако на практике часто приходится изобретать велосипед заново, и порой это оправдано:
- Если у приложения есть требования к работе в реальном времени, то использование библиотек со сборщиком мусора или любым другим недетерминированным поведением недопустимо.
- Когда язык программирования позволяет игнорировать ошибки, возникает риск того, что они не будут корректно обрабатываться в используемых библиотеках. Это может приводить к желанию переписать библиотеку с нуля, чтобы гарантировать надёжную обработку всех ошибок. Однако язык Zig разработан с акцентом на то, чтобы обработка ошибок была наиболее очевидным и простым решением для программиста. Поэтому часто можно быть уверенным в том, что библиотеки, написанные на Zig, обеспечивают надлежащую обработку всех ошибок.
- Сегодня можно с уверенностью сказать, что C — это самый универсальный и кроссплатформенный язык программирования. Любой язык, который не предоставляет возможности взаимодействия с кодом на C, рискует остаться в тени. Zig стремится стать новым стандартом, одновременно упрощая соответствие C ABI (Application Binary Interface) для работы с внешними функциями и внедряя механизмы безопасности, которые помогают избежать типичных ошибок при разработке.
Менеджер пакетов и система сборки для существующих проектов
Zig — это не только язык программирования, но и целый набор инструментов. В его составе есть система сборки и менеджер пакетов, которые будут полезны даже для традиционных C/C++ проектов.
Zig можно использовать не только для написания кода вместо C или C++, но и в качестве замены инструментам вроде autotools, CMake, Make, SCons, Ninja и других. Помимо этого, Zig предлагает менеджер пакетов для управления нативными зависимостями, а система сборки Zig актуальна даже в том случае, если весь код вашего проекта полностью написан на C или C++. Например, портирование ffmpeg на систему сборки Zig позволяет компилировать ffmpeg на любой платформе для любой другой платформы с использованием единственного исполняемого zig-файла размером 50 МиБ. Для проектов с открытым исходным кодом такая возможность сборки из исходников — и даже кросс-компиляция — может стать критически важным фактором в привлечении и удержании ценных разработчиков.
Системные менеджеры пакетов, такие как apt-get, pacman, homebrew и другие, играют ключевую роль в удобстве пользователей, но зачастую не полностью удовлетворяют потребностям разработчиков. Для проектов с открытым исходным кодом сложный процесс сборки может стать серьёзным препятствием. В проектах на C/C++ наличие зависимостей особенно критично – особенно в Windows, где отсутствует централизованный менеджер пакетов. Даже сборка самого Zig часто затруднительна для многих из-за зависимости от LLVM. Zig предлагает способ подключения проектов к нативным библиотекам напрямую, без необходимости полагаться на то, что в системном менеджере пакетов пользователей установлена нужная версия. Этот подход практически гарантирует успешную сборку проектов с первой попытки, независимо от используемой системы и целевой платформы.
Другие языки программирования имеют свои менеджеры пакетов, но они не устраняют назойливые системные зависимости так эффективно, как это делает Zig.
Zig может заменить систему сборки проекта на более удобный язык с использованием декларативного API. Этот API также обеспечивает управление пакетами, что фактически позволяет зависеть от других библиотек на C. Возможность иметь такие зависимости даёт возможность создавать более высокоуровневые абстракции, что в свою очередь способствует распространению и переиспользованию кода.
Простота
C++, Rust и D настолько насыщены различными функциями, что это может отвлечь от разработки. Вместо того чтобы сосредоточиться на разработке самого приложения, вы можете оказаться погружёнными в изучение тонкостей самих языков.
В отличие от них, в Zig нет макросов, но это не делает его менее мощным. Этот язык позволяет выразить сложные программы в ясной и ненавязчивой форме. Например, в Rust есть макросы, такие как format!
, которые требуют поддержки на уровне компилятора. В Zig же аналогичная функция из стандартной библиотеки реализована без какой-либо специальной поддержки на уровне компилятора.
Таким образом, Zig помогает сосредоточиться на разработке приложения, минимизируя отвлекающие факторы, связанные с освоением языка.
Инструментарий
Zig можно скачать из раздела загрузок. Zig предоставляет бинарные архивы для Linux, Windows и macOS. Вот что включает в себя такой архив:
- устанавливается простым скачиванием и распаковкой архива, не требуя системной конфигурации
- статически скомпилирован, поэтому отсутствуют зависимости во время выполнения
- поддерживает использование LLVM для оптимизированных релизных сборок, одновременно используя собственные бэкенды для более быстрой компиляции
- предоставляет возможность экспортировать код на C
- готов для кросс-компиляции на большинство основных платформ
- содержит исходный код для libc, который будет динамически компилироваться по мере необходимости для любой поддерживаемой платформы
- включает систему сборки с поддержкой конкурентности и кэширования
- компилирует код на C и C++ с поддержкой libc
- обеспечивает полную совместимость с командной строкой GCC/Clang через
zig cc
- содержит компилятор ресурсов для Windows