Почему я перестал писать новый код на C в пользу Zig

Why Zig over C for Systems Programming

Автор, для которого C остаётся любимым языком, объясняет, что именно в C отнимает у него время и как Zig решает эти проблемы. Речь идёт об очистке ресурсов без goto, срезах, знающих свою длину, ошибках, которые нельзя проигнорировать, comptime вместо препроцессора и проверке переполнения. При этом он честно признаёт: Zig не спасает от use-after-free, а его нестабильность требует правок при смене версий.

Я пишу этот пост не потому, что считаю C плохим; C — мой любимый язык программирования! Я пишу его потому, что больше не хочу писать новый код на C.
  1. uecker

    Zig — приятный язык, но эти статьи всегда сравнивают новые языки со старым C без использования современных инструментов. Большинство перечисленных преимуществ можно получить и в C, если вы готовы принять современные инструменты и стиль: проверка знакового переполнения: санитайзеры, ошибки, которые нельзя игнорировать: [[nodiscard]], тип среза, который знает свой размер: variably-modified types или span-типы.

    Defer также скоро появится в компиляторах C, но лично я всё ещё считаю обработку ошибок через goto более понятной.

    (У Rust есть преимущество в временной безопасности памяти, но это не относится к Zig.)

  2. variadix

    Я остановился на диалекте C++ для системного программирования после долгого использования C. Он в основном похож на C, и я отключаю исключения, RTTI и т.д., но использую шаблоны, перегрузки, методы, лямбды и т.п.

    У меня есть собственная объектная система с методами-конструкторами, которые все возвращают код статуса, так что исключения не нужны, своя стандартная библиотека для контейнеров, буферизованного ввода-вывода, сетевого взаимодействия, хеширования, математики, потоков, асинхронности и т.д.

    C++ даёт все возможности, чтобы эффективно построить свой собственный язык, и вы можете использовать первоклассные инструменты, поскольку C++ широко поддерживается. LLM также значительно упрощают создание и тестирование целой стандартной библиотеки самостоятельно.

  3. 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: […]

  4. pron

    Кроме того, Zig как минимум так же выразителен, как C++ (но без неявности и общей сложности языка, которые затрудняют рецензирование некоторого чувствительного кода, где нужно точно видеть, какие операции выполняются), и он лучше поддерживает арены, чем C/C++/Rust.

  5. drnick1

    Все эти новые языки нечитабельны по сравнению с C. Если, как и мне, вам нравится явный код вроде for (size_t i = 0; i <= n; i++) total += a[i];, все эти новые языки не подойдут.

  6. kombine

    D и Nim имели те же возможности годами, кстати.

  7. qweqwe14

    Rust всё равно захватит все эти нишевые языки. Да, Rust — жизнеспособная замена C и C++ в долгосрочной перспективе. Zig просто пытается быть золотой серединой, которая, как мы уже установили, не работает. Zig особенно неактуален по сравнению с Rust в эпоху LLM.

  8. program

    > Ошибки, которые нельзя игнорировать

    Меня устраивает объявить макрос с `__attribute__((warn_unused_result))` для GCC и Clang и `[[nodiscard]]`, когда код на C позже компилируется с C++ (или ничего).

Ещё за этот день

2026-11-13