Комментарии 57
Хм.. Как будто для такой задачи go может еще лучше подойти, что думаете?
Неплохая статья
Удивлён, что версия на Zig получилась короче - он более многословный.
Для парсинга аргументов на С стоило тоже взять готовую библиотеку. Сравнение было бы корректнее
На С тоже элементарно делается defer макросами
Но все же Zig это язык уже нового поколения.
И вообще - это перевод? Потому что актуальный Zig 0.16
Почему для С ТОЖЕ стоило взять готовую библиотеку? Вроде в zig все решено с помощью std. И где вы видели актуальный Zig 0.16? На официальном сайте указана версия 0.15.2
Я вот на 0.14 сижу и доволен ;) Ну либо автор написал код какое-то время назад, а статью только сейчас.
К сожалению до релиза 1.0 далеко, когда выйдет уже и не нужен будет наверное.
Так ему уже 10 лет, они его полируют, ломают (-ли) совместимость, чтобы к релизу выдать что-то готовое... Не уверен насчёт достоверности, но, вроде, в этом году выйдет стабильный релиз v1.0
LLM выдала со ссылкой на Telegram-канал: "По некоторым прогнозам, версия 1.0 языка программирования Zig выйдет не раньше 2029 года".
Я читал, что автор - перфекционист и стремится создать надёжный инструмент. Однако процесс версионирования может затянуться на следующие 10 лет. За это время многое может измениться в разработке ПО, учитывая быстрый прогресс LLM.
Если у вас есть точная информация о дате выхода версии 1 из roadmap, буду признателен.
Язык С не прибит гвоздями к Makefile, там уже много разных альтернатив придумали.
И мне интересно, почему zig , а не c++?
Чтобы не страдать с Makefile, cmake, automake, premake и прочими сборочными извращениями, которые современные языки решают обычной сабкомандой init. Уж лучше какой-нибудь cppfront взять.
Ох уж эти страдатели, которые маке не осилили)
Даже и пытаться осиливать не стали, разу взяли Shake ;)
Вот его-то как раз проще всего освоить. В сравнении с Zig, который требует только Zig для контроля зависимостей это определённо жирный минус. А уж если один пишет на cmake, второй на autotools, а третий либу собирает через conan веселье умножается пропорционально и можно шутить что неосиливатель маке не осилил ещё каких-то три-четыре сборочных системы. Это ж куда важнее, чем просто писать прогамму на языке.
Кстати, попадаются проекты на С, которые используют Zig чисто как систему сборки.
Он ещё и кэширует (в принципе, так же как и маке) сборку автоматически.
А вот с подгрузкой сторонних пакетов лично не пробовал, но вроде возможности есть.
Так что в плюс. Но каких то особенных прорывов нет.
используют Zig чисто как систему сборки.
Это как раз один из их способов саморекламы. Многие хвалят компилятор Си у zig за понятные ошибки да и вообще за удобную интероперабельность с Си. Похожую поддержку встречал только у D. Одно время build.zig пытались контрибутить во многие популярные репы на си. Кто-то принял, кто-то послал на три буквы, чтобы не заниматься поддержкой ещё одного языка в проекте. Слышал, что некоторые сишные проекты решили использовать целиком инфраструктуру zig для сборки кода, но живьем не встречал.
Rust читал месяц. Крутой, но borrow checker для утилиты которая читает файл и пишет в stdout - избыточен.
Так ведь если утилита в одном исходном файле, то borrow checker вы даже не заметите.
В C мне надо было писать
fclose(file)перед каждымreturn, а их у меня было четыре разных в этой функции.
Множественный return вообще не очень хорошая практика. Корректнее использовать единую точку выхода.
К сожалению, в языках где нет возможности определять блок
on-exit;
// выполняем все необходимые перед выходом действия
...
return;куда передается управление поде каждого return в функции, или хотя бы поддержки сабрутин (аналог подпрограммы в бейсике - отдельный блок кода внутри функции, вызываемый без создания нового уровня стека и не имеющий собственных аргументов и локальных переменных), позволяющий организовать точку выхода
begsr srExit;
// выполняем все необходимые перед выходом действия
...
return;
endsr;и позволяющий вместо
if (...) return;использовать
if (...) exsr srExit;использование единой точки выхода может потребовать определенной организации кода для избежания многоуровневых if !error.
Функции возвращают -1 или NULL при ошибке, смысл ошибки - в errno или нигде.
Это, мягко говоря, не так. Есть понятие "структурированной ошибки". В простейшем виде выглядит вот так:
typedef struct Qus_EC
{
int Bytes_Provided;
int Bytes_Available;
char Exception_Id[7];
char Reserved;
char Exception_Data[]; /* Varying length */
} Qus_EC_t; Передается аргументом в функцию. На выходе достатчоно проверить
if (Qus_EC.Bytes_Available) {
// функция заполнила ошибку
...
};Bytes_Provided тут - размер структуры (определяется размером блока дополнительных данных Exception_Data) ; Bytes_Available = 0 - ошибки не было, Bytes_Available > 0 - ошибка была; Exception_Id - код ошибки; Exception_Data - блок дополнительных данных произвольного размера, связанных с конкретной ошибкой.
Остальное тоже весьма спорно. Например
В C у меня было иначе: одна функция
process_fileделала всё сразу. Добавить новый формат лога = переписывать её всю
А зачем было делать именно так? Почему не разбить эту функцию на несколько более мелких? Например, "диспетчер", определяющий формат лога + набор парсеров под разные форматы? Новый формат - пишем парсер и добавляем его в таблицу парсеров диспетчера.
Такое ощущение, что кинулись писать на С особо не продумав что и как, поплясали с бубном на граблях и потом уже, получив понимание что и как, переписали все на Zig. И 90% "бонусов" не от того, что Zig лучше, а от пришедшего понимания тонкостей задачи.
Это всё круто, но как ты ошибки не возвращай, всегда будет одна проблема - компилятор не скажет, где ты забыл обработать ошибку. Один раз не проверил что вернула функция, у тебя с большой вероятностью сегфолт, который ещё попробуй исправь. zig такого не позволяет. Хотя, на самом деле, zig тоже не очень безопасный в этом плане. Забыл освободить ресурс через defer и прога падает или память утекает, но в нём хотя бы есть DebugAllocator и стек вызовов, что позволяет ошибки отловить достаточно рано. То есть на корню все проблемы системного программирования zig не решает, но он однозначно делает многие вещи лучше C.
Ну я не спорю. Писать на С требует тренировки мозга - многое приходится в голове держать. Это только годами практики нарабатывается до автоматизма.
В этом плане Rust интереснее, хотя и требует привыкания к заложенным в нем концепциям.
А структурированная ошибка - это целая концепция на уровне системы.
Есть т.н. message file - таблица где содержится код ошибки, ее текстовка (с возможностью подстановки параметров), уровень серьезности (например, 0 - информация, 10 - предупреждение, 20 - критическая). Есть системное API куда передается вот эта структура, содержащая код и значения параметров для подстановки. API по коду находит в message file данную ошибку, берет ее текстовку, подставляет параметры и возвращает полный текст и уровень серьезности.
Разрабатывая сое ПО вы всегда можете расширять message file добавляя туда характерные для вашего ПО ошибки со своими кодами. Функция в общем случае возвращает получилось/не получилось. Если не получилось, вы можете или анализировать ошибку по коду, или же дернуть API и получить полный текст (уже с подставленными параметрами) и дальше выводить его на экран, в лог или еще куда вам нужно.
Плюс эта же ошибка может пробрасываться в виде исключения (языкового или системного, если таковые поддерживаются).
Как-то так по итогу оно и вываливается во втрое больший код, потому что сначала нужно написать систему, чтобы было хорошо - то арену добавь, то работу с массивами скопипасти, то обработку ошибок допиши...
Если речь идет о долгосрочном проекте который потом придется поддерживать и развивать, то оно того стоит.
Ну или если себе строишь набор рабочих инструментов на долгое время.
И в любом серьезном проекте работа с ошибками занимает достаточно большую часть. Если не делать как современные вебклепатели - на все одна ошибка "что-то пошло не так, попробуйте как-нибудь потом"
Если речь идет о долгосрочном проекте который потом придется поддерживать и развивать, то оно того стоит.
Из чего следует необходимость учить ещё один какой-нибудь язык для того чтобы собирать код на си. Можно конечно использовать какой-нибудь nob, чтобы иметь только компилятор в зависимостях, но там свои квирки и segafult приветы будут.
Ну если по хорошему, то использовать более одного языка в одном проекте - нормально.
Я вот пишу как минимум на двух (то, что в прод идет). Ну и Rust немного для себя... Правда, языки у меня сильно разные. Один хорош для работы с БД и всяких коммерческих расчетов (это основной), но если что-то более низкоуровневое, то там С/С++ лучше.
Часто бывает что один кусок задачи лучше и быстрее решается на одном языке, другой - на другом.
Одна точка выхода:
if (error1) goto one_exit;
...
if (error2) goto one_exit;
...
if (error3) goto one_exit;
...
one_exit:
...release actions...Если кому то режет глаз goto, то:
while (TRUE) {
if (error1) break;
...
if (error2) break;
...
break;
}
...release actions...Есть еще вариант с исключениями, но лично я их использование считаю дурным тоном.
Есть еще вариант с исключениями, но лично я их использование считаю дурным тоном.
Да, согласен. Исключения штука ресурсоемкая. В высоконагруженных системах использование их стоит свести к минимуму. Только в случае крайне необходимости.
Когда условий много делал примерно так:
for (stage=0; stage < MAX_STAGE; stage++) {
switch (stage) {
case 1:
... блок операций где может возникнуть прерывающая ошибка ...
break;
case 2:
... блок операций где может возникнуть прерывающая ошибка ...
break;
...
}
if (error) break;
}Чуть хуже с точки зрения производительности, но лучше с точки зрения читаемости. Плюс то, что находится в блоках case вынести в отдельные функции
Но суть, да, в том чтобы обеспечить единую точку входа и единую точку выхода.
do {
... блок операций где может возникнуть прерывающая ошибка ...
break;
... блок операций где может возникнуть прерывающая ошибка ...
break;
} while (0);А почему не так?
Потому что вам придется после каждого блока проверять if (error) break;
А здесь на каждом обороте цикла отработает один блок (тот, что идет по соотв. номеру) и после этого проверится ошибка.
Все равно switch/case выглядит более громоздко.
Придется дробить на блоки не по смыслу, а до первой ошибки.
Громоздко когда после каждого блока будете if error... проверять.
А дробление на логические блоки - это совершенно нормальный паттерн. Выполнили блок - проверили ошибку, если удачно - идем дальше
Такие блоки по уму надо вообще в отдельные функции выводить. Чтобы не получить одну функцию на 10 экранов.
И тогда нормально будет если каждая такая функция возвращает успешно/не успешно + ошибку в параметре. Тогда вообще просто:
if (func1(...) && func2(...) && ... && funcN(...)) {
// успешное выполнение
} else {
// была ошибка - разбираемся где
}Ну не знаю, для меня вот это:
for (stage=0; stage < MAX_STAGE; stage++) {
switch (stage) {
case 1:
... блок операций где может возникнуть прерывающая ошибка ...
break;
case 2:
... блок операций где может возникнуть прерывающая ошибка ...
break;
...
}
if (error) break;
}выглядит намного более громоздко, чем вот это:
do {
... блок операций где может возникнуть прерывающая ошибка ...
if (error) break;
... блок операций где может возникнуть прерывающая ошибка ...
if (error) break;
...
} while (0);Наброшу немного за Rust
1)
Ошибки через типы -
!T
В Rust еще круче есть - Result<T>: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#recoverable-errors-with-result
2)
Кросс-компиляция из коробки -
zig build -Dtarget=x86_64-windowsбез тулчейнов
В Rust: https://crates.io/crates/cargo-zigbuild
3)
Маленький проект собирается за секунду
В Rust - за доли секунд
4)
Rust - это безопасность ценой сложности
Это ложно. Корректный код на Rust в миллиарды раз проще писать, чем на C, Zig и, даже, Go. Проверено
5)
1089 строк на Zig
На Rust вышло бы и того меньше за счет отсутствия defer (это "фичей" назвать трудно, если честно; только в ироничном ключе), функционального подхода и удобнейших инструментов для работы со строками
6)
Ошибки компилятора длинные
В Rust прекрасные ошибки от компилятора, часто содержащие примеры исправлений. На зависть многим языкам
7)
Стоит ли переходить на Zig в 2026?
Почти в любом случае - нет, железобетонно. Нужен язык, использовать который можно без боли, быстрый, без gc - Rust, без серьезных альтернатив
Рекомендую, если целью является обучиться крутому инструменту production-ready и стать востребованным разработчиком, изучить Rust и/или Go. C - deprecated, Zig - тупик.
ИМХО
PS: вспомнилось мне интервью на youtube-канале ThePrimeTime с Алексеем Кладовым о Zig и его фичах. Зашла речь про то, что Алексею понадобилось быстренько написать небольшой инструмент для работы с большим строковым файлом. Он ответил, что выбрал Rust. Очень показательно )
https://www.youtube.com/watch?v=V8Bg55lTUCw&t=3701s
С Rust есть "всего лишь одна маленькая проблема" - нужно вникнуть в заложенные в нем концепции, принять их, немного перестроить мозги. И после этого работа с ним становится простоя и естественной.
Но на это требуется время, сразу с наскока может не получиться - пока не проникнешься, все будет казаться мучительно и больно.
На Rust вышло бы и того меньше за счет отсутствия defer
Скорее всего не стало бы меньше а +/- столько же, сколько и у Zig, если писать единым rs. Обработка result наверняка заполнила эти пробелы.
Парсил несколько гигов хмлников на шарпе за 5 секунд, от проца и ио конечно зависит, но чистый си это всегда геморрой конечно. Не знаю кто такой зиг, но если бы дожал на си, было бы быстрее.
Читаю статью - и мозг постоянно цепляется за нейросетевые паттерны текста. Автор, признайся честно - нейросетка писала статью?
пишешь CLI-утилиты, системные инструменты, что-то встраиваемое
Ну не знаю, не знаю, как-то сомнительно если честно. Для перового и второго лучше взять что-то из троицы C++, Rust, Go. Для встраиваемых систем можно использовать Rust если уже пишешь на нём, если же до этого писал на C/C++, смысла переходить особо нет, максимум какую головную боль вылечишь - безопасный код писать можно, но ты не обязан.
И моя личная головная боль - сертификация по EN 50128, где по факту C это стандарт, он просто самый предсказуемый и проверяемый, и сертификация под него дешевле, а Ferrocene пока не дотягивает. Тем более что зачастую есть только компилятор для C99, о существовании C++ разработчики не слышали, и приходится терпеть терпеть, как например для 8-16-разрядных контроллеров. А с учётом жизненного цикла кода в 25 лет, Rust или что-то другое я увижу не скоро.
сертификация по EN 50128
C23 самую большую дырку с массивами закрыл. И с многопоточкой поработали
Он раньше пройдёт все сертификации.
А Раст туда попадет не раньше чем no-panic и nostd стандартизуют
Zig выигрывает у многих для эмбеда за счёт идеологии - никаких скрытых действий (аллокаций, паник, исключений итп) и кросс компиляции из коробки.
А если переписать эту утилиту с zig на Python, получится еще в два раза меньше строк кода.
И время работ с гигабайтными файлами сократится с пары секунд до жалких минут, да.
И производительность упадет раз так в 1000.
Интересный вопрос. Думаю, не в 1000 раз, а меньше.
Куда уж меньше ))
Если серьезно, Python по бенчмаркам в 10 раз медленнее PHP, а это оба интерпретируемые языки, куда им до скомпилированного машинного кода.
Плюсовый иострим может с этим поспорить =)
Ну, в PHP еще с версии 7.4 появился плохонький JIT-компилятор. В версии 8.3 его переписали полностью и включили по умолчанию (раньше нужно флаг выставлять в php.ini), и он прямо хорош, кроме шуток. Основная боль PHP — блокирующий ввод-вывод, т. е. программа на PHP большую часть времени ждет, когда ей данные прилетят или когда придет результат вывода.
Теперь ближе к Zig. Мне тут нужно было распарсить JSON больше 120 Мб, предварительно собрав словарь из 200+ HTML-файлов (тоже парсинг по сути), и всё это положить в БД SQLite.
Так вот, программа на PHP с включением транзакции при добавлении записей в БД отработала за 5.4 секунды. А версия на Zig, написанная из чистого интереса, — 2.7 секунды. Разница для этой задачи вообще не драматическая, при том, что я и на том, и на том языке пишу одинаково хорошо. Однако версия на PHP заняла в разработке меньше часа, а версия на Zig — почти два, просто потому, что многие вещи, которые на Zig приходилось выписывать руками, в PHP встроены в язык. Т. е. вдвое более высокая производительность кода на Zig компенсировалась вдвое большим временем разработки.
Такие дела.
Очевидно, системный язык плохо приспособлен для прикладных задач.
Может и так, хотя Zig позиционируют, как язык общего назначения с уклоном в низкоуровневое программирование. У него, кстати, вчера вышла версия 0.16.0 и в первой строке Release Notes написано: «Zig — это язык программирования общего назначения и набор инструментов для создания надежного, оптимального и многократно используемого программного обеспечения.»
Сразу скажу что сегфолты - это когда прога пытается залезть в память к которой ей нельзя.
Тут надо уточнить, что если сегфолта нет (то есть прога залезла в память, к которой ей можно), это все равно не исключает ошибки. Потому как не факт, что это именно та часть памяти, которая подразумевалась.
Нет hidden control flow -
defer file.close()прямо в коде, ничего за кулисами
В языках без gc деструктор выглядит безопаснее и понятнее
Язык без GC и объектов. Хм, дайте подумать....
Приятнее, да=) для птиц
На самом деле gc нужен там, где есть активная работа с динамической памятью. Которая в целом отрицательно сказывается на производительности есть это высоконагруженная система (чтобы хоть как-то это нивелировать начинаются пляски с кастомными аллокаторами).
Поэтому языков, где динамическая работа с памятью сведена к минимуму, хватает. И GC там просто не нужен.
Почему-то все боятся написать правду. Я пишу на Си с 1985 года, я написал на нём миллионы строк, от ядра операционной системы до ядра Яндекс.Маркета. Писал на Си СУБД, графику, обработку данных, код для микроконтроллеров и супермашин - всё писал. 40 лет писал.
Ну пора уже в 2026 году сказать честно: для 1985 года он был прорывом. Сегодня Си - это сущий кошмар, а не язык, и писать на нём надо только под дулом пистолета.
Если кто-то хочет возразить, пусть сначала прочтёт, поймёт и объяснит результат вычисления вот такого выражения:
a = 2["abcdefg"];
Если смысл его вам не очевиден - не торопитесь выступать в защиту Си. :)
(И как человек, который прыгал до неба когда вышел C++ - увы, его тоже довели до состояния невменяемости, и его тоже надо избегать.)
Ну и, почему-то, все забыли https://dlang.org/
Кстати, современная Java вообще позволяет запускать однофайловый исходник без компиляции, как Питон, и писать на ней легко и просто. Особенно если привык к Си.

Zig вместо C: пишем высокопроизводительный CLI-инструмент и заменяем 3000 строк C-кода