
Ландшафт языков программирования постоянно меняется, у них появился новый конкурент — Zig, о котором много говорят.
Особенно всколыхнулось программирование систем — благодаря уникальному сочетанию производительности, безопасности и простоты Zig. Это современная, надежная альтернатива Cи, C++ и даже Rust или Go, ею обеспечиваются полная совместимость с Cи и точное управление памятью, при этом она доступна для новичков.
Присмотримся к ней повнимательнее.
Я не специалист по программированию систем, но мне нравится, что делается в Rust, в прошлом я увлекался им и читал книги.
Golang — идеальное решение для небольших инструментов, которые не ограничиваются сценариями оболочки.
Эта статья о том, как программирование систем воспринимается со стороны разработчика из управляемых языков, таких как Java и Swift.
Краткая история Zig
Zig создан Эндрю Келли, первый публичный выпуск состоялся в 2016 году.

В отличие от других языков системного программирования с аналогичным назначением, он вырвался на несколько лет вперед: Rust появился в 2012 году, а Golang в 2009.
Несмотря на молодость Zig и сильную конкуренцию, для него быстро нашлась ниша. Cейчас два самых известных проекта на Zig — это среда выполнения JavaScript / универсальный инструмент Bun и недавно выпущенный платформенно независимый эмулятор терминала Ghostty.
За несколько лет Zig превратился в интересный язык для тех, кто ценит контроль, безопасность и простоту программной разработки.
Забавный факт: название Zig выбрано по скрипту 4-х буквенных слов, которые начинаются на Z. Понятия не имею, куда делась одна буква, вероятно, было Zigg и от повторяющегося символа автор избавился.
Что такое Zig?
Это универсальный язык для программирования систем.
Он появился из желания создать язык более прагматичный, чем Си. Его философия проектирования — сбалансировать для разработчиков производительность, безопасность и надежность, а также простоту и легкость применения:
- Прагматизм:
Простота для разработчика, отсутствие излишней сложности и только минималистичный синтаксис, доступность для новичков и опытных разработчиков. - Оптимальность:
Высочайшая производительность во время выполнения за счет написания самого естественного и выразительного кода. - Надежность:
Оптимальность — номер один, но и безопасность совсем рядом, без ущерба для высокой эффективности. Разработчики имеют полный контроль над важным для производительности функционалом, таким как ручное управление памятью. - Прозрачность:
В Zig обеспечивается эксплицитность кода, избегаются скрытые потоки управления, которыми затеняется поведение программы. Например, нет скрытого выделения памяти, поскольку осуществляется явное управление всей памятью, или нет неявного приведения типов.
Имеется уникальный функционал, например, фантастическая совместимость с Cи и comptime — о них позже — и другой ожидаемый от современного языка функционал вроде интегрированного инструментария и легкой компиляции в код целевой платформы.
Вот обязательный Hello, World!:
const std = @import("std");
pub fn main() void {
const lang = "Zig";
std.debug.print("Hello, {s} World!\n", .{lang});
}
Знакомый синтаксис для тех, кто работал на Cи-подобных языках.
Основной функционал
Чтобы представить все преимущества Zig, рассмотрим основные аспекты языка.
Нет скрытых потоков управления
Отсутствие скрытых потоков управления — фундаментальный принцип Zig.
В других языках немало функционала, из-за которого поведение кода делается непредсказуемым или затеняется фактический поток данных, с чем сладить становится труднее:
- Неявное преобразование/приведение типов:
Автоматическое преобразование типов данных чревато нежелательным поведением: потерей точности, ошибками преобразования или переполнениями. Допустимы только безопасные и однозначные приведения типов. - Перегрузка операторов:
Перегрузка стандартного оператора привлекательна обширными функциональными возможностями, но ею изменяется поведение по умолчанию и она чревата неожиданными вызовами функций или побочными эффектами. - Исключения и скрытая обработка ошибок:
Исключения — мощный инструмент, но ими затеняется поток управления, поскольку функции могут завершаться преждевременно, не будучи явно обнаруживаемыми в коде. - Автоматическое управление памятью:
Сборка мусора и скрытые выделения памяти — это функционал безопасности, при котором сложность убирается в среду выполнения. Однако при этом также появляется непредсказуемость, снижается производительность. - Неявное async:
Функционалом вроде async/await скрывается фактический поток выполнения, из-за чего трудно понять последовательность операций, особенно при отладке.
В Zig эти проблемы предотвращаются благодаря отсутствию скрытых потоков управления.
Отсутствие неявного преобразования/приведения типов
При приведении значение преобразуется в другой тип. Неявное, автоматическое приведение конкретных типов вроде примитивов осуществляется во многих языках.
В Zig же приведение типов выполняется, если преобразование безопасно и однозначно, например для константных значений.
Типы должны преобразовываться явно:
const std = @import("std");
pub fn main() void {
const a: f32 = 23.0;
const b: i32 = 42;
// Безопасное преобразование без потери точности
const sum: u8 = a + b;
std.debug.print("Sum: {}\n", .{sum});
const x: f32 = 23.5;
const y: i32 = 42;
// Небезопасное из-за потери точности
const unsafe: u8 = x + y;
std.debug.print("Sum: {}\n", .{unsafe});
}
// Ошибка компилятора
// src/main.zig:16:26: error: преобразование значения с плавающей точкой «65.5» в тип «u8» предотвращается дробной частью
// const unsafe: u8 = x + y;
//
Если безопасное приведение типа невозможно, явное приведение включается через встроенные функции вроде @intFromFloat.
Отсутствие перегрузки операторов
Следующий шаг к прозрачности и очевидному потоку управления — отсутствие перегрузки операторов.
Но ведь перегрузкой операторов создаются интересные предметно-ориентированные языки, например Swift. И все же, почему ее нет в Zig, абсолютно понятно: при перегрузке операторов затеняется поток кода и требуется «знать», как типы взаимодействуют через перегруженные операторы.
Четкая обработка ошибок
Исключения избегаются, а ошибки являются частью сигнатуры типа функции и обрабатываются посредством объединения ошибок и ключевыми словами try и catch, так что обработка ошибок становится явной частью логики.
Ошибки в Zig возвращаются как часть специального типа-объединения, обозначаемого префиксом !. Этим типом явно указывается, что в функции возвращается значение или ошибка:
// Определяются возможные ошибки
const Errors = error {
DivideByZero,
// ...
};
fn divide(a: u8, b: u8) !u8 {
if (b == 0) {
return Errors.DivideByZero;
}
return a / b;
}
Сейчас при помощи throw, например, в Java или Swift минуется обычный поток управления, и текущая функция преждевременно завершается. Здесь же одним ключевым словом return возвращается ошибка либо допустимое значение. И, в отличие от Golang, кортежа не требуется.
Обработка ошибок явная и выполняется двумя способами:
- Вызывается
divideсtry, и ошибка добавляется в сигнатуру внешней функции. - Ошибка обрабатывается при помощи
catch.
Первым способом ошибка подтверждается и в источнике вызова вверх по стеку обрабатывается:
const std = @import("std");
pub fn main() void {
// Чтобы распространить ошибку вверх по стеку, используется «try»
const result = try divide(42, 23);
std.debug.print("Result: {}\n", .{result});
}
Сначала этот код не компилируется:
src/main.zig:6:20: error: expected type 'void', found '@typeInfo(@typeInfo(@TypeOf(main.divide)).@"fn".return_type.?).error_union.error_set'
const result = try divide(42, 23);
^~~~~~~~~~~~~~~~~~~~~~
src/main.zig:3:15: note: function cannot return an error
pub fn main() void {
Даже функции main необходимо объявлять потенциальную ошибку посредством возвращаемого типа !void, если они ею не обрабатываются. Проблема в том, что выше main ничего нет, поэтому приложение аварийно завершается.
Разберемся с ошибкой так:
const std = @import("std");
pub fn main() void {
// Ошибка обрабатывается с помощью «catch»
const result = divide(23, 0) catch |err| {
std.debug.print("Caught an error: {}\n", .{err});
return;
};
std.debug.print("Result: {}\n", .{result});
}
Объединение ошибок обрабатывается напрямую, и try не требуется, поэтому для объявления возможной распространяемой ошибки !void не нужен.
Ключевым словом catch либо возвращается допустимый результат, либо ошибка становится доступной в блоке обработчика через присваивание |<название переменной>|. Затем в блоке обработчика необходимо вернуть значение согласно сигнатуре. В случае с void, как здесь, ничего не возвращается.
Если ошибок несколько, их элегантнее обработать в switch, например, для HTTP-обработчика:
self.handle(action, req, res) catch |err| switch (err) {
HttpError.NotFound => {
res.status = 404;
res.body = "Resource Not Found";
res.write() catch return false;
},
else => self.handleOtherErrors(req, res, err),
}
Подход Zig к обработке ошибок прекрасно вписывается в общие цели:
- Прозрачность:
Все возможные пути ошибок делаются явными в сигнатурах типов функций. - Предсказуемость:
Нет скрытого потока управления, каждая потенциальная ошибка должна обрабатываться явно. - Безопасность:
Обеспечивается тщательная обработка ошибок, снижается риск появления необработанных ошибок, чреватых сбоем программы.
Разработчикам из языков с «традиционными» исключениями типы объединений ошибок Zig поначалу покажутся странными. Однако благодаря детализированности и обязательному применению здесь повышается четкость, системы становятся более надежными, сопровождаемыми и отказоустойчивыми.
Ручное управление памятью
В Zig обеспечивается полный контроль над выделением и высвобождением памяти в куче, а значит, память управляется эффективно и предсказуемо.
В отличие от Cи и C++, в Zig появляются распределители памяти, явно передаваемые функциям во избежание скрытых операций с памятью. Для очистки памяти ее выделение сопровождается шаблоном defer:
const std = @import("std");
pub fn main() !void {
// Получается распределитель памяти
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
// Очистка обеспечивается передачей распределителя и «defer»
const array = try createData(allocator, 10);
defer allocator.free(array);
// Выводятся данные
for (0.., array) |idx, value| {
std.debug.print("arry[{d}]={d}\n", .{ idx, value });
}
}
fn createData(allocator: std.mem.Allocator, count: usize) ![]u32 {
// При помощи распределителя выделяется достаточный объем памяти
var array = try allocator.alloc(u32, count);
// Инициализируются данные
for (0.., array) |idx, _| {
array[idx] = @intCast(idx * idx);
}
return array;
}
Передача распределителей — ключевой принцип stdlib, обязательный к применению в коде.
Но зачем вообще передавать распределитель, а не создавать его при необходимости?
По тем же причинам:
- Прозрачность:
Отсутствие скрытого выделения внутри функций, отчего использование ресурсов эффективнее, предсказуемее и понятнее. - Гибкость:
Источником вызова выбирается оптимальный для его сценария распределитель или даже создается собственный.
Для Zig явное управление памятью — в приоритете, попутно устраняются многие подводные камни скрытых операций с памятью, обнаруживаемые в других, не столь строгих языках. Поначалу кажется утомительным, зато получаются полный контроль и гибкость.
Необязательная стандартная библиотека
Другая особенность — в отличие от большинства современных языков, стандартная библиотека абсолютно необязательна: не хотите использовать stdlib, не включайте ее в двоичный файл. Поэтому Zig идеален для встраиваемых или ограниченных по ресурсам сред.
Еще один необязательный элемент этого паззла — libc. Он желателен для сред, где недоступен или отсутствует: в ядрах низкоуровневой системы, пользовательских средах выполнения или двоичном коде «голого железа».
Это очередной пример эксплицитного подхода Zig ко всему. Нет скрытых зависимостей и даже stdlib: в конечный двоичный файл включен явно используемый код, и ничего кроме. Это большое преимущество перед языками вроде Go или Rust, стандартные библиотеки которых тесно интегрированы и обычно содержат неиспользуемый или нежелательный функционал, что по умолчанию чревато разрастанием двоичных файлов.
Детализированный контроль над тем, что включать в код, предоставляется в Zig разработчику. Так что это решение гибкое, но осознанное.
Таким упрощенным подходом в Zig удобнее нацеливаться на WebAssembly с небольшими, быстро загружаемыми модулями.
Интегрированная система сборки
Инструментарием любого языка создается желаемая продуктивность, им же она и ломается.
Например, проблема с инструментами JavaScript, которые постоянно развиваются/изменяются/ломаются или частое разочарование при работе на Java в Gradle могут стать настоящим бедствием для этих языков.
Во многих новых языках этот хаос пытаются укротить включением собственных инструментария и управления зависимостями, предоставить готовое решение для сглаживания общего процесса.
В Rust имеется cargo, а в Go — свои модули/форматирование/тестирование, встроенные в команду go, и даже современными подходами к JavaScript/TypeScript, такими как Deno, предоставляются все необходимые инструменты для насущных потребностей проекта. Таким образом, в любом современном языке должно быть что-то для облегчения жизни разработчиков, и Zig не исключение.
В Zig имеются встроенный диспетчер пакетов и система сборки для упрощения платформенно независимых сборок и управления зависимостями. Этими тесно интегрированными в язык инструментами предоставляется первоклассная поддержка сборки, тестирования и компиляции.
Скрипты сборки пишуются в самом Zig, по умолчанию в файле build.zig:
const std = @import("std");
pub fn build(b: *std.Build) void {
const exe = b.addExecutable(.{
.name = "my-app",
.root_source_file = b.path("src/main.zig"),
.target = b.host,
});
b.installArtifact(exe);
}
Иметь инструментарий, в котором используется сам язык программирования, очень удобно: разработчикам приходится осваивать только один язык. Кроме того, скрипты сборки здесь строго типизированные и простые в проверке.
Запуск скрипта сборки: zig build.
Вообще-то для минимальной настройки скрипт сборки не нужен: zig build-exe src/main.zig.
Компиляция в код целевой платформы тоже выполняется непосредственно, ведь целевая архитектура и операция задаются напрямую: zig build-exe --target x86_64-linux-gnu src/main.zig.
В целом система сборки — это сложная штука, как и все другие системы сборки, но особых усилий с самого начала здесь не требуется. Ее документация отделена от справки по языку.
Полная совместимость с Cи
Для языков системного программирования совместимость с Cи — обязательное условие, большинством из них она в той или иной степени поддерживается. Zig отличается полной совместимостью.
Использование библиотек Cи не сложнее импортирования заголовка, дополнительных привязок не требуется.
Возьмем приложение на Си:
const stdio = @cImport({
@cInclude("stdio.h");
});
pub fn main() void {
// Возвращаемое значение должно быть подтверждено
_ = stdio.printf("Hello from C and Zig!\n");
}
Чтобы обеспечить разрешение необходимых символов во время компиляции, Zig интегрируется с компоновщиком Cи.
Чтобы найти заголовок, в процессе сборки должно выполниться связывание
libcили ее эквивалента.
Совместимость с Cи взаимная: ключевым словом export очень легко генерировать функции, вызываемые из Cи:
export fn add(a: i32, b: i32) i32 {
return a + b;
}
// На Cи:
// extern int add(int x, int y);
Компилирование в общую библиотеку: zig build-lib -dynamic add.zig.
Благодаря полной взаимозаменяемости с Cи, Zig идеален для работы над проектами на Cи и их модернизации — использованием их из Cи либо написанием более нового кода на Zig и применением его из Cи:
- Дополнительных инструментов или привязок не требуется.
- Zig автоматически справляется со сложностями разрешения символов, преобразования типов, связывания.
- Части больших проектов на Cи легко и постепенно заменяются более безопасным, сопровождаемым, предсказуемым кодом на Zig.
Comptime
Среди разнообразного функционала выделяется мощный и уникальный comptime, код с ним выполняется во время компиляции.
На фоне традиционных языков, где логика компиляции и времени выполнения разделена, в Zig имеется двухуровневый процесс компиляции, при котором генерируются данные, выполняются вычисления или даже условно включается код.
В отличие от многих макросистем, это не просто замена текста, а фактическое выполнение кода и вставка результата в двоичный файл. При таком подходе вычисления переносятся из среды выполнения в компиляцию, отчего сокращаются накладные расходы времени выполнения и увеличивается гибкость при проектировании программ.
Сначала создадим таблицу поиска:
const std = @import("std");
// Генерируется таблица поиска во время компиляции.
const table = comptime generateTable();
fn generateTable() []u8 {
return [_]u8{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
}
pub fn main() void {
std.debug.print("Table at compile time: {any}\n", .{table});
}
Результат включается в библиотеку, любые вычисления во время выполнения убираются.
comptime не ограничивается чем-то простым, все сгодится там, где известны все переменные, например сохранение результата рекурсивного вычисления факториала:
const std = @import("std");
fn factorial(n: u32) u32 {
return if (n == 0) 1 else n * factorial(n - 1);
}
pub fn main() void {
const result = comptime factorial(10);
std.debug.print("Factorial: {d}\n", .{result});
}
Добавление, например, std.time.sleep не выполнится, так как функция comptime больше не детерминирована во время компиляции, и компилятором указывается где и почему:
/home/ben/.zig/lib/std/os/linux.zig:1528:9: error: unable to evaluate comptime expression
@intFromPtr(request),
^~~~~~~~~~~~~~~~~~~~
/home/ben/.zig/lib/std/os/linux.zig:1528:21: note: operation is runtime due to this operand
@intFromPtr(request),
^~~~~~~
/home/ben/.zig/lib/std/Thread.zig:80:55: note: called at comptime from here
switch (linux.E.init(linux.clock_nanosleep(.MONOTONIC, .{ .ABSTIME = false }, &req, &rem))) {
~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.zig:4:19: note: called at comptime from here
std.time.sleep(@as(u64, 5) * std.time.ns_per_s); // Безопасно, но требуется явное приведение
~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.zig:10:38: note: called at comptime from here
const result = comptime factorial(10);
~~~~~~~~~^~~~
src/main.zig:10:20: note: 'comptime' keyword forces comptime evaluation
const result = comptime factorial(10);
^~~~~~~~~~~~~~~~~~~~~~
// ...Фрагмент...
Другой сценарий применения comptime — условные ветвления кода:
const std = @import("std");
const builtin = @import("builtin");
pub fn main() void {
if (comptime builtin.os.tag == .macos) {
// Этот код будет включен, только если целевой ОС является macOS.
return;
}
std.debug.print("I'm not on macOS, that's for sure\n", .{});
}
Так оптимизируются двоичные файлы, поскольку после компиляции код для вычислений или неиспользуемые условные ветвления кода больше не включаются. И, конечно же, вычислений во время выполнения больше не требуется.
В других языках тоже это делается: текстовыми макросами либо несколькими файлами. Но здесь еще и включается непосредственно в любой файл — просто замечательно.
Zig, Rust и Go
Zig, Rust и Go — современные языки программирования, в которых устраняются ограничения и недостатки предыдущих, таких как C/C++. У каждого из этих трех языков своя философия, сильные и слабые стороны, существенно различаются подходы Zig, Rust и Go к производительности, безопасности, простоте и сценариям применения.
Zig выделяется на их фоне следованием новыми или как минимум интересными путями.
Вот сравнение языков по ключевым характеристикам:
Производительность
Zig ориентирован на явное ручное управление памятью с полным контролем над ее выделением и высвобождением. Отсутствием сборки мусора обеспечивается предсказуемая производительность с малой задержкой. Благодаря возможности исключать стандартную библиотеку и даже libc Zig идеален для минималистичных приложений, где важна производительность, особенно в средах с ограниченными ресурсами.
В Rust высокая производительность обеспечивается абстракциями с нулевой стоимостью и безопасной работой с памятью на уровне модулей благодаря средству проверки заимствований. Моделью памяти Rust устраняется неопределенное поведение без ущерба для производительности во время выполнения, поэтому Rust — отличный выбор для систем, в которых важна безопасность, он остается конкурентом с высокой производительностью. Однако средство проверки заимствований — это сложная штука с крутой кривой обучения.
В Go, как языке с поддержкой сборки мусора, часть номинальной производительности приносится в жертву простоте. Автоматическая обработка памяти — отличное подспорье, особенно если не нужна абсолютная производительность. Но оно чревато непредсказуемой задержкой, поэтому в сравнении с другими языками Go менее пригоден для систем реального времени или систем, в которых важна производительность.
Безопасность
В Zig безопасность обеспечивается явным контролем обработки ошибок и управления памятью. Скрытые потоки управления не обусловлены выделением памяти. Многое из неявного функционала отсутствует. Ничто не происходит без вашего ведома и принятия решения об этом.
Благодаря правилам владения и заимствования Rust славится строгими гарантиями безопасного использования памяти. Таким образом средством проверки заимствований и компилятором предотвращаются многие проблемы вроде гонок данных, использования освобожденной памяти и переполнений буфера во время компиляции. Однако этот функционал безопасности не обходится без многочисленных сложностей, страшных для новичков.
В Go главный акцент сделан на простоте, предоставляется базовое безопасное использование памяти со сборкой мусора. Но, по сравнению с другими языками, здесь это все еще чревато паниками во время выполнения и более легкими пропусками проблем.
Простота
В Zig предпочитается минималистичный дизайн, избегается сложный функционал вроде макросов, async/await, перегрузки операторов. Синтаксис понятный и доступный, поэтому Zig удобен для восприятия и сопровождения без ущерба для его мощи. Это хороший выбор и для начинающих, и для экспертов.
В Rust предоставляется обширная функциональность: времена жизни, типажи и т. д. Это «мощный, но сложный» язык. Из-за средства проверки заимствований и сложных абстракций здесь крутая кривой обучения, особенно для новичков.
Go спроектирован простым и удобным для применения, ориентированным на продуктивность разработчиков. Сделано это за счет отсутствия расширенного функционала: ручного управления памятью, макросов, строгих гарантий безопасности. Тем не менее из этих языков Go — самый доступный, но и наименее универсальный для низкоуровневого программирования.
Zig: аргументы «за»
Почему стоит выбрать Zig для очередного проекта?
Zig выделяется на фоне языков программирования сбалансированным сочетанием необходимых для системного программирования эксплицитности, простоты и мощи. С общей философией максимальной прозрачности и предсказуемости этого языка разработчик получает полный контроль над кодом, избегая при этом неопределенного поведения, скрытого потока управления или неявных последствий во время выполнения.
Универсальность Zig привлекательна для разработчиков всех основных языков системного программирования:
- Разработчики на Си избегают опасностей неопределенного поведения, сохраняя при этом полный контроль. Возможен даже поэтапный подход.
- Разработчики на Rust, устав бороться со средством проверки заимствований, устремляются к более приемлемой кривой обучения, сохраняя при этом максимальную мощь и безопасность.
- Разработчиками Go востребованы контроль над управлением памятью и меньшие затраты времени выполнения для приложений, где важна производительность.
Из производительности, безопасности и простоты в языках обычно выбирается два компонента, в Zig же сбалансированно сочетаются все три.
Zig: аргументы «против»
А в каких ситуациях Zig выбирать не стоит?
Очевидная причина — недостаточная зрелость. Несмотря на наличие проектов вроде Ghostty или Bun, пока не выпущена версия Zig 1.0 и даже рекомендуется ночная сборка. Это означает большие изменения API и несовместимости версий в проектах, привязанных к конкретной версии.
Например, не удалось собрать Ghostty из исходного кода, так как требовалась версия ниже (0.13), чем доступная на тот момент ночная (0.14). Потом не получилась сборка LSP, которая сначала «жаловалась» на некорректную ночную версию, а после обновления компилятора выдавала ошибки во время компиляции.
Нечто подобное ожидается до «официального» выпуска версии 1.0. Тем не менее его трудно «продать» компаниям, которые не располагают выделенными ресурсами, чтобы регулярно адаптироваться, или застревают на какой-то версии. В Rust же версию 1.0 выпустили десять лет назад.
Другая проблема — экосистема — тоже связана с незрелостью Zig.
Каждым языком со временем накапливаются собственные инструменты и библиотеки, с ним много чего происходит, он постоянно разрастается. Благодаря полной совместимости с Cи также устраняются обнаруживаемые недостатки. Однако у Rust и Golang экосистема обширнее, особенно в бизнес-среде, здесь больше доступных специалистов.
Еще один замечательный, при определенных условиях, аспект Zig — стремление к простоте. Каждый раз, когда ради простоты уменьшается сложность, здесь исключается функционал, который в других языках принимается как должное.
И, например, отсутствие встроенной модели конкурентности, при том что на Go имеются горутины, а в Rust — безбоязненный параллелизм, становится препятствием для перехода на Zig.
Тем не менее большую часть отсутствующей функциональности можно реализовать самостоятельно, к тому же со временем в экосистеме многое появится. Но для проекта с интенсивным параллелизмом или конкурентностью все сложнее, и предпочтение может быть отдано другому языку.
И все-таки Zig стоит попробовать.
Зачем Zig, если им не пользоваться?
Изучать новые языки программирования стоит, даже если не использовать их непосредственно в профессиональной среде.
В каждом новом языке предлагаются не только уникальная философия проектирования, парадигмы и подходы к решению проблем, но и новое видение задач, представление о методах и концепциях, которые не распространены в привычном языке.
Чем шире понимание, тем в итоге оптимальнее пишется код и не происходит зацикливания на чем-либо.
Читайте также:
- Эффективные шаблоны архитектуры программного обеспечения
- Альтернатива Docker Desktop, которая работает быстрее
- Переход с VS Code на Neovim: повысьте свою продуктивность
Читайте нас в Telegram, VK и Дзен
Перевод статьи Ben Weidig: Why Everyone Talks About Zig





