Почему я перестал писать новый код на C в пользу Zig
Why Zig over C for Systems Programming
Автор, для которого C остаётся любимым языком, объясняет, что именно в C отнимает у него время и как Zig решает эти проблемы. Речь идёт об очистке ресурсов без goto, срезах, знающих свою длину, ошибках, которые нельзя проигнорировать, comptime вместо препроцессора и проверке переполнения. При этом он честно признаёт: Zig не спасает от use-after-free, а его нестабильность требует правок при смене версий.
Я пишу этот пост не потому, что считаю C плохим; C — мой любимый язык программирования! Я пишу его потому, что больше не хочу писать новый код на C.
- uecker
Zig — приятный язык, но эти статьи всегда сравнивают новые языки со старым C без использования современных инструментов. Большинство перечисленных преимуществ можно получить и в C, если вы готовы принять современные инструменты и стиль: проверка знакового переполнения: санитайзеры, ошибки, которые нельзя игнорировать: [[nodiscard]], тип среза, который знает свой размер: variably-modified types или span-типы.
Defer также скоро появится в компиляторах C, но лично я всё ещё считаю обработку ошибок через goto более понятной.
(У Rust есть преимущество в временной безопасности памяти, но это не относится к Zig.)
- variadix
Я остановился на диалекте C++ для системного программирования после долгого использования C. Он в основном похож на C, и я отключаю исключения, RTTI и т.д., но использую шаблоны, перегрузки, методы, лямбды и т.п.
У меня есть собственная объектная система с методами-конструкторами, которые все возвращают код статуса, так что исключения не нужны, своя стандартная библиотека для контейнеров, буферизованного ввода-вывода, сетевого взаимодействия, хеширования, математики, потоков, асинхронности и т.д.
C++ даёт все возможности, чтобы эффективно построить свой собственный язык, и вы можете использовать первоклассные инструменты, поскольку C++ широко поддерживается. LLM также значительно упрощают создание и тестирование целой стандартной библиотеки самостоятельно.
- asveikau
Только из первого примера:
struct user *user_new(const char *name, size_t n_groups)
{
struct user *u = malloc(sizeof *u);
if (u == NULL)
goto fail;
u->name = strdup(name);
if (u->name == NULL)
goto fail_user;
u->groups = calloc(n_groups, sizeof *u->groups);
if (u->groups == NULL)
goto fail_name;
return u;
fail_name:
free(u->name);
fail_user:
free(u);
fail:
return NULL;
}
Я думаю, можно представить C лучше, чем это. Мне нравится сосредотачиваться на одном блоке очистки и использовать тот факт, что free(NULL) — это no-op.
struct user *user_new(const char *name, size_t n_groups)
{
struct user *u = calloc(sizeof *u, 1);
if (u == NULL)
goto fail;
u->name = strdup(name);
if (u->name == NULL)
goto fail;
u->groups = calloc(n_groups, sizeof *u->groups);
if (u->groups == NULL)
goto fail;
return u;
fail:
if (u)
{
free(u->name);
free(u->groups);
}
free(u);
return NULL;
}
Хотя обратите внимание, что это предполагает, что calloc(sizeof *u, 1) инициализирует указатели в NULL; насколько я помню, предположение, что битовый шаблон NULL состоит из всех нулей, технически противоречит стандарту, но любая система, для которой вы, вероятно, будете компилировать, всё равно будет работать.
Если вы следуете шаблону «освобождать NULL-указатели можно», вы также можете повторно использовать функцию free: […]
- pron
Кроме того, Zig как минимум так же выразителен, как C++ (но без неявности и общей сложности языка, которые затрудняют рецензирование некоторого чувствительного кода, где нужно точно видеть, какие операции выполняются), и он лучше поддерживает арены, чем C/C++/Rust.
- drnick1
Все эти новые языки нечитабельны по сравнению с C. Если, как и мне, вам нравится явный код вроде for (size_t i = 0; i <= n; i++) total += a[i];, все эти новые языки не подойдут.
- kombine
D и Nim имели те же возможности годами, кстати.
- qweqwe14
Rust всё равно захватит все эти нишевые языки. Да, Rust — жизнеспособная замена C и C++ в долгосрочной перспективе. Zig просто пытается быть золотой серединой, которая, как мы уже установили, не работает. Zig особенно неактуален по сравнению с Rust в эпоху LLM.
- program
> Ошибки, которые нельзя игнорировать
Меня устраивает объявить макрос с `__attribute__((warn_unused_result))` для GCC и Clang и `[[nodiscard]]`, когда код на C позже компилируется с C++ (или ничего).