Cを愛する著者が、新しいコードをZigで書く理由

Why Zig over C for Systems Programming

UNIXカーネルからmanページまでCで書かれている世界を認めつつ、著者は新規コードにZigを選ぶ。errdeferによる後始末、長さを持つスライス、無視できないエラー、comptime、整数オーバーフローの検査など、Cで時間を奪われる部分をZigがどう解決するかをコード例で示す。安全性の穴や言語の不安定さといった正直なトレードオフにも触れる。

私はCが悪いと思ってこの記事を書いているわけではない。Cは私の好きなプログラミング言語だ! 書いているのは、もうCで新しいコードを書きたくないからだ。
  1. uecker

    Zigはいい言語だが、こういう記事はいつも現代的なツールを使わない古いスタイルのCと新しい言語を比較する。挙げられている利点のほとんどは、現代的なツールとスタイルを採用する気があればCでも得られる:符号付きオーバーフローチェックはsanitizers、無視できないエラーは[[nodiscard]]、サイズを把握するスライス型は可変修飾型やspan型で実現できる。

    DeferもまもなくCコンパイラに登場するだろうが、個人的にはgotoによるエラー処理の方がまだ明確だと思う。

    (Rustは時間的メモリ安全性で優位だが、これはZigには当てはまらない。)

  2. variadix

    長い間Cを使ってきた後、システムプログラミング用にC++のある方言に落ち着いた。ほとんどCライクで、例外やRTTIなどは無効にしているが、テンプレート、オーバーロード、メソッド、ラムダなどは使っている。

    例外を必要としないように、すべてステータスコードを返すコンストラクタメソッドを備えた独自のオブジェクトシステムを持ち、コンテナ、バッファ付きI/O、ネットワーキング、ハッシュ、数学、スレッディング、非同期などのための独自の標準ライブラリも持っている。

    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はこれよりもっとうまく表現できると思う。私はクリーンアップブロックを1つに絞り、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してもよい」というパターンに従えば、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は単に中間を目指しているだけで、それがうまくいかないことはすでに分かっている。LLM時代においてZigはRustに比べて特に無関係だ。

  8. program

    > 無視できないエラー

    GCCとClang向けに`__attribute__((warn_unused_result))`で、Cコードが後にC++(または何もなし)でコンパイルされるときに`[[nodiscard]]`でマクロを宣言するのは私には問題ない。

この日のほかの記事

2026-11-13