Comments 43
Хм.. Как будто для такой задачи 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++?
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);А почему не так?
Наброшу немного за 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, получится еще в два раза меньше строк кода.
Сразу скажу что сегфолты - это когда прога пытается залезть в память к которой ей нельзя.
Тут надо уточнить, что если сегфолта нет (то есть прога залезла в память, к которой ей можно), это все равно не исключает ошибки. Потому как не факт, что это именно та часть памяти, которая подразумевалась.
Zig вместо C: пишем высокопроизводительный CLI-инструмент и заменяем 3000 строк C-кода