世界を動かす技術を、日本語で。

Zigは実際のCLIツールにおいてRustよりも実用的に感じる

概要

  • CLIツール 開発では、最近 Zig を優先的に選んでいる理由の解説
  • スタックとヒープ のメモリ管理の基本と、それぞれの特徴
  • RustのBorrow Checker の強みと制限、特にCLIツール開発時の課題
  • Zigの手動メモリ管理 とシンプルな設計の利点
  • CLIツールの安全性 の本質と、開発者エルゴノミクス(生産性・効率性)への影響

なぜ最近CLIツールにはZigを選ぶのか

  • スタック は一時的なデータ保存領域、LIFO方式で高速なアクセスが特徴
  • ヒープ は動的メモリ割り当て用領域、サイズ可変で長期間データを保持可能
    • スタックは 関数の引数・ローカル変数・戻りアドレス の保存に利用
    • ヒープは オブジェクトやランタイム生成データ構造 の保存に利用
  • RustのBorrow Checker はコンパイル時にメモリ安全性を保証
    • ヌルポインタ参照・ダングリングポインタ・二重解放 などを防止
    • ただし 開発者の設計ミスやロジックバグ までは防げない
  • RustでのCLI開発 は、所有権やライフタイムの制約でコードが複雑化しやすい
    • 例えば、ノート管理CLIで 参照と可変操作の衝突 に悩まされる事例
  • Zig は手動メモリ管理と明示的なアロケータ利用による柔軟な設計が可能
    • ライフタイムや所有権の制約が少なく、直感的なコード記述
    • defer文 によるリソース解放の自動化
    • comptime機能 によるコンパイル時最適化や静的解析が可能

CLIツールにおける「安全性」とは何か

  • メモリ安全性 はソフトウェア安全性の一要素に過ぎない
    • 予測可能な動作 :異常入力にも期待通りに振る舞うこと
    • クラッシュやサイレントなデータ破損の回避 :エラー発生時もユーザーに適切なフィードバック
    • パフォーマンス管理 :大規模データでも応答性を維持
    • 機密データの保護 :一時ファイルの権限やデータ漏洩対策
    • 攻撃耐性 :バッファオーバーフローやインジェクション攻撃の防止
  • RustのBorrow Checker はメモリ管理の安全性に特化
    • データ競合・可変エイリアシングの排除
    • コンパイル時保証 で多くのバグを事前に検出
    • ただし ロジックバグや予期せぬ動作 は防げない
    • 複雑な所有権やライフタイム管理 が小規模CLIでは過剰となる場合も

Zigの安全性とシンプルさのアプローチ

  • 手動メモリ管理アロケータ による柔軟なリソース制御
    • std.heap.GeneralPurposeAllocator などの標準アロケータ利用
    • カスタムアロケータ の実装も容易
  • defer文 でスコープ終了時に自動的にリソース解放
  • 開発者の規律 が安全性維持の鍵
    • Rustは コンパイラに安全性維持を委ねる 設計
    • Zigは 開発者に自由と責任を与える 設計
  • comptime によるコンパイル時処理でパフォーマンスや安全性向上
  • CLIツール開発 では、Zigの シンプルさ・柔軟性・直感的な設計 が大きなメリット

開発者エルゴノミクスと生産性

  • 開発効率・可読性・保守性 を重視したいCLIツール開発
  • Rust は安全だが 学習コストやコードの複雑化 が課題
  • Zig直感的な設計・短い開発時間・容易なメンテナンス が可能
  • どちらを選ぶかはユースケースや開発者の好みに依存
    • 安全性の担保方法と開発体験のバランス選択が重要

このように、CLIツール開発においては Zigのシンプルさと柔軟性 が際立つメリットを持ち、 Rustの厳格な安全性 は一部の用途で過剰となる場合もある。 開発者自身の規律とユースケースに応じて言語選択を行うこと が、最適なCLIツール開発への近道となる。

Hackerたちの意見

Zigの利点は、Cプログラマーのように考え続けられるところだと思う。これは素晴らしいことかもしれないけど、ある意味では習慣の問題でもあるよね。経験豊富なRustプログラマーは、借用チェッカーと戦う時間を無駄にしない。彼らのコードは、最初からうまく機能するように書かれているから。Rustを使い始めてしばらく経つと、借用チェッカーを気にしてコードを「再構築」する必要はなくなる。だって、「ああ、この2つの変数は同時に変更する必要があるから、別々に保存しよう」って考えるようになるからね。「オブジェクトスープ」っていうアプローチはRustではうまくいかないけど、慣れ以外では他の選択肢より根本的に簡単なアプローチではないよ。

経験豊富なRustプログラマーは、借用チェッカーと戦う時間を無駄にしない。 俺の経験では、あなたの言うことが正しい理由は、経験豊富なRust開発者がArcをあちこちに散りばめて、実質的に自動ガベージコレクションに切り替えているからだよ。だって、1) 静的にチェックされたメモリ管理は、ほとんどの非自明なデータ構造には制約が厳しすぎるし、2) 静的チェッカーを喜ばせるために非自明なことを始めるときに必要なライフタイムの輪っかは、人間の理解を超えてるから。

100%同意。俺は主にJavaScript/TypeScriptのバックグラウンドからRustに来たけど、使っていたイディオムやアプローチがそのままRustに移行できたよ。

経験豊富なRustプログラマーは、借用チェッカーと戦う時間を無駄にしない。 本物のスコッツマンなら、借用チェッカーに混乱することはないよ。俺は、Arcを多用したり、cloneやcopyをあちこちで使っているオープンソースのRustプロジェクトをたくさん見てきた。

借用チェッカーで問題があったのはいつが最後か覚えてないな。ジュニアの解決策は.clone()、より良いのは&(参照)で、本当に必要なら使い始めることもできる。どの関数が何を消費するかには少しイライラするけど、LLM時代がこれを助けてくれた。俺の不満は、トレイトの実装方法や、AWSが自分たちのライブラリのエラーを実装したやり方が純粋に狂ってることだな。

Zigの利点は、Cのようにコードを書く感覚を持ちながら、言語やツールに安全性を高める要素があるところだと思う。例えば、Zigにはオプショナルがあって、nil参照の問題を排除できる。もう一つの例は、テスト中にデバッグ用やカスタムのアロケーターを渡すことができて、悪いメモリアクセスやリソースリークを検出するための様々なランタイムチェックがあること。Zigのデザインには明示的なインターフェースやトレイトがないことに関していくつか問題があるけど、採用のしやすさから見ても、実用的な言語だという意見には同意するよ。

経験豊富なRustプログラマーは、借用チェッカーと戦う時間を無駄にしない そうだね、彼らはいつ諦めるべきかを知ってる。

経験豊富なRustプログラマーは、借用チェッカーと戦う時間を無駄にしない 「借用チェッカーと戦う」っていう考え方は、借用チェッカーが純粋にレキシカルなライフタイムしか理解していなかった時代のものだと思う。だから、明らかに正しいことを書いたのに、それが本当に正しい理由を説明するために戦わなきゃいけなかった。私が2021年にRustを学んだ時には、もうそんなのは昔の話だったけど、Rustが「借用チェッカーと戦う」っていうイメージは根付いてしまった。今、PythonやC、JavaScriptだけのバックグラウンドの人にとっては、Rustを学ぶのは本当に大きな調整が必要だと思う。私には自然に感じられたけど、ほとんどの人はそうじゃないだろうね。でも、Cプログラマーでも、これまでの経験がほとんどなければ「借用チェッカーと戦う」ことはあまりないと思う。スパリウスな可変参照を通じてポインタを作れないっていう診断?それは借用チェッカーじゃないよ。関数の結果を使わないっていう警告?それも借用チェッカーじゃない。今、「Rustではソフトウェアをコンパイルするためにすべての診断を読まなきゃいけなかった」って言うのは、「借用チェッカーと戦う」よりもヒーロー感が薄いけど、もし本当にそうなら、もっと勇気を持って表現する方法を考えなきゃいけないかもね。

私はCプログラマーのようには考えないんだ。問題は、Java/Python/Goプログラマーのように考えてしまうことで、常にガベージコレクターが背後でメモリを掃除してくれることに慣れてしまっているから。Rustは時々醜い/クレイジーなコードになることもあるけど、全体的には好きだよ。だって、見つけにくいメモリエラーを作ることがないって感じられるから。もちろん、私の(Rust)アプリをクラッシュさせるコードを書くこともあるけど、今のところそれらはすべてデバッグして修正するのがすごく簡単なエラーだった。でも、Zigはまだ試してないんだ。コンパイル時のメモリ使用保証は同じようにあるのかな?

観客が「エルゴノミクスは定量化できないから重要じゃない」と受け入れてしまう限り、上で示したような手のひら返しは混乱を招くよね。「この椅子は絶対に崩れない保証があるよ。ちょっと快適さが欠けてて、重いかもしれないけど、ほとんどのアスリートはそれに慣れて気づかないから!」記事を引用すると、> 「今のところ、Rustは開発者のエルゴノミクスが悪いけど、安全なメモリを持つソフトウェアを作れる。一方、Zigは開発者のエルゴノミクスが良くて、少しの規律で安全なメモリを持つソフトウェアを作れる。Rustコミュニティはこのトレードオフについて率直に話すべきだ。安全性はエルゴノミクスが悪くなるというのは普遍的なトレードオフだ。スケートボードにヘルメットをかぶって乗るときも、プログラミングするときも、セックスのときも同じだ。」代わりに、経験則やあいまいな言葉での議論が多い。「私が話すほとんどの人は、経験が少ない場合を除いて、あまり問題を抱えていないようだ。」これはすごいレトリックだね。一文でエルゴノミクスの議論を主観性を否定することで却下し、反対する人は愚かだと暗に示している。

Rustに対する批判の多くは(有用な批判もたくさんあるのは認めるけど)、結局「自分が慣れてるやり方とは違う考え方/訓練が必要だから、難しい」ってことに集約されるんだよね。で、他のやり方の方が簡単だって言うけど、それは単に彼らにとって馴染みがあるからそう思ってるだけ。もっと多くの人が「Simple made easy」ってトークを見るべきだよ。https://www.youtube.com/watch?v=SxdOUGdseq4

Zigを好きになりたいけど、Dがまだ存在していて、C++の代替として求めているものが全部揃ってる感じなんだ。業界全体がもっと早くDを採用してくれればよかったのに。Zigの構文はちょっと変わってるし、Rustは特にプログラマー向けのツールで業界の多くを飲み込んでるよね。Goも同様で、ほとんどのクラウドプロバイダーを支えていて、AIに関してはPythonの次に人気の選択肢だし。

Rustの前はGo対Dが話題だったのを覚えてる。Dの本を買って勉強してた時にGoが発表されて、そっちに心を奪われた。私にとって決定的だったのは標準ライブラリで、Goを使うのが本当に楽だった。あとは、'double'の代わりに'int64'みたいな名前を使うのが、私の脳には合ってたからかな。

Dは素晴らしい選択肢だけど、残念ながら大衆に受け入れられるキラーアプリがなかったね。

借用チェッカーの価値を過小評価していると言おうと思ってた。無効なメモリアクセスを保証してくれるから。でもその後、こう付け加えたんだね。> 「つまり、基本的に借用チェッカーはコンパイル時にしか問題を捕まえられないけど、開発者がメモリのライフタイムや複雑すぎる所有権を誤解している根本的な問題を解決するわけではない。」コンパイラーは、あなたが従おうとしているルールを強制することしかできない。良いパターンを教えることはできないし、悪い設計の選択から救ってくれるわけでもない。Rustを書いていた短い間、ライフタイムの注釈が間違っているとは思わなかった。ちょっと面倒だとは感じたけど、自分の言いたいことは言えてると思ってた。使い慣れるまでには時間がかかるだろうし、静的型のように、いつかは自然になるんだろうね。それに、unsafeを使わないコードは、同じメモリに2つのスレッドが同時に書き込むことはできない。この記事のフルタイトルは「ZigがRustよりも実用的に感じる理由」。CLIツールが特別な理由はないと思う。記事には良いポイントもあるけど、RustがCVEを防ぐ力を無効にするわけではないと思う。RustやZigは、特定の人にとっては使い方が違うかもしれないし、時間とデータがそれを証明するだろう。個人的には、C/C++やZig、Rustのフルスピードが必要なことはあまりないから、GC言語がたくさんあるし。他のプロジェクトに貢献する時は、言語を選べないから、RustでもZigでもC/C++でも使えるのは嬉しい。

CLIツールが特別な理由がわからない。 大きくなったり、複数人のチームが必要になったりしないからだよ。CLIツールは一回で終わることが多い。つまり、「ZigはCのようにスケールしないから、大きくて長生きするコードベースには他のものを使え」って言ってるんだ。この記事が言いたいのは、Zigはあなたを大人扱いしてくれるけど、Rustは赤ちゃんの面倒を見てくれるってこと。これは昔のJavaに対する感情に似てる。でも実際には、ほとんどのコードベースは賢くある必要はなくて、面倒を見てもらう必要があるんだよね。

とにかく、安全でないコードは同じメモリに二つのスレッドが同時に書き込むことはできない。もう少し複雑なんだけど。基本的に、コンパイラから実際に助けが欲しいと思うのは、メモリの順序の問題だけだ。Rustはその特定の競合するメモリ書き込みを安全にすることを選んだんだ。

手動でコードを書くときはZigが大好きだったけど、最近は個人プロジェクトでもAIを使ってコードを書くことが増えてきた。そういう状況では、AIが複雑な構文を処理してくれるから、Rustをもっと使いたいと思ってる。あと、Rustのエコシステムは大きいから、このコミュニティに留まりたいな。> 開発者はバカじゃない。俺はよく気が散るし、AIはバカだから、厳格な言語の方が俺もAIも余計なバカなことをしないで済むんだよね。

私はよく気が散るんだけど、建物の入り口のすぐ横にオフィスがあるから、これが役に立ってる。いつも訪問者が来るし、ドアを閉めてるとノックされることも多い。AIや厳格な言語は、私にとっては砂漠の水のように、集中力を保つための素晴らしいツールなんだ。

これは本当にひどい意見だね。「型はいらない」っていう先週の投稿と同じくらい。開発者として覚えておきたいのは、すべてのプログラムがそんなに「安全」である必要はないってこと。実際、全然必要ない。私たちはみんな、危険なソフトウェアを愛して育ったんだ。スターフォックス64、MSペイント、フルーティーループス…悲しい真実は、開発者が仕事に追われて、なぜこの業界に入ったのかすら忘れてしまっていること。アンドリュー・ケリーがDAWを書くための良い言語がなかったからZigを書いたってどこかで読んだことがあるけど、ああいうことにはすごく向いてると思う!Zigで自分が好きなクールなクリエイティブソフトを作ればいいし、メモリバグにこだわる人はそのまま怒ってればいい。みんな知ってるけど、メモリバグがスーパーマリオワールドをより良くしたんだよ。

え、混乱してるんだけど、私の意見が悪いのはメモリの安全性が重要じゃないからってこと?

開発者として忘れないでほしいのは、すべてのプログラムがそんなに「安全」である必要はないってこと。「安全性」ってのは「私のプログラムは私が言うことを意味する」っていう短縮形に過ぎない。安全でないのは意味不明な言葉。アートとしてのコードを書く理由はたくさんあるけど(例えばルイス・キャロルみたいに)、ほとんどのプログラムはアートを目指してるわけじゃない。コンピュータを通じて何かを達成しようとしてるんだから、意味不明なコードは逆効果だよ。書いたことを意味しないなら、ソフトウェアは目標を達成するための間違った手段に思える。こういう観点からソフトウェアが本当に必要か疑問だし、意味不明なものが高次のレベルで意味を持つ場合(IOCCCや詩みたいに)でも、意図的で慎重に作られるべきだと思う。Rustでは逃げ道を使ってこれを達成できるけど、やることの芸術的な価値については何も言わないよ。君が主張してるのは、制御されていない意図しない意味不明さがプラスの属性だってことだよね。それは受け入れがたい議論だと思う。もし魔法の杖で全てのコードを安全にできるとしたら、誰もがそうしたいと思うんじゃない?スーパーマリオワールドのスピードランには何の影響もないし、バイナリパッチで任意のコード実行入力と同じことができるからね。私たちはただ、片方がチートで片方がそうじゃないっていう半ば非合理的な信念を持ってるだけだよ。

先週末、メモ管理のためのシンプルなCLIツールを作ったんだ。~/.notesを解析してノートのリストを作り、文字列をそのリストの参照にマッピングするタグインデックスを構築するんだ。簡単そうだよね?でもRustではそうはいかない。借用チェッカーが、新しいノートを追加しようとした瞬間に既存のノートへの参照を持っているとブロックする。可変性と借用が衝突して、ライフタイムが出てきて、突然コンパイラの周りにコードを再構築する羽目になる。実際のコードを見てみたいな!Rustのコードを想像すると、複雑な借用チェッカーや参照の問題はあまり思い浮かばない。こんな感じかな:struct Note { filename: String, // もしかしたら: contents: String } // notesへのインデックス用の新しい型 struct NoteIdx(usize); struct Notes { notes: Vec, tag_refs: HashMap> } ポインタの代わりにインデックスを保存するんだ。これが遅くなる可能性はほとんどないよ。usizeのインデックスもポインタも、ほとんどのハードウェアで64ビットだからね。確かに一つ余分なメモリのデリファレンスがあるけど、notesがキャッシュにある可能性が高いから、実際のパフォーマンス差はあまり見られないと思う。魔法じゃないけど、ノートを追加したり削除したりする時にインデックスを間違えることはある。だけど安全性は高いよ。インデックスを間違えると、意図しないメモリの場所に書き込む代わりに、範囲外エラーが出るからね。まあ、安全性にこだわらないとしても、明確で考えやすいし、論理的に考えるのも楽だし、printfデバッグもやりやすい。「このタグはノート3、10、190で言及されている、ああ、これらを出力してみよう。」生のポインタを読むよりはずっといいよね。何か見落としてるかな?こういうタスクはRustのコードを書くときに毎日のように出てくる普通のパターンなんだ。こんな普通のロジックには生の参照を保存しないよ。アロケーターや非同期ランタイムを書くときには必要だけどね。有名なところでは、非同期は.awaitの呼び出し間でスタックローカルな状態を保存するために自己参照構造体が必要で、だからPinの仕組みが存在するんだ。

ベクターへのポインタは、新しい要素を追加して再配置が発生すると、いつでも無効になる可能性があります(ここで無効なポインタを使うと、ミリが怒鳴ると思います)。

RustやZigでCLIツールを書く理由がわからないな。I/OはGCよりもボトルネックになることが多いし、実際、ゲーム開発やデータベース、他のメモリ集約型アプリケーション以外でGCの嫌悪感が理解できない。GoやPythonを使えばいいじゃん。人々はメモリ安全性と非メモリ安全性の間に偽の二項対立を作ろうとするけど、本当はGCとノーGCの問題なんだ。メモリ安全性はどちらにしても難しい。GCを使わない理由を自分に正当化する時間をもっと使うべきで、どのGCなしの言語を使うかにあまり時間をかけるべきじゃないよ。(「楽しいからGCなしにする」なら、そもそもその投稿は必要ないよ。楽しいものを使えばいいんだから!)

Pythonは、異なるマシンでPythonを動かすのが本当に面倒だからダメ。Goも、貧弱な型システムが理由でダメ。

GoはCLIツールには素晴らしい選択肢だよ(言語自体はあまり好きじゃないけど)。PythonのCLIアプリは、依存関係が多いと配布が大変になることがある。だからRustやZigも魅力的なんだ。Goのように、単に/usr/local/binにコピーできる静的にコンパイルされたバイナリを作るのが簡単だしね。

実際、ゲーム開発以外でのGCの嫌われ方が理解できない。ほとんどのモバイルゲームはGC(Unityのil2cpp)を使って実装されてるし、それに良いGCですらない、Boehmだし。

インスタントスタートアップタイムは本当にいいよね。違いがはっきり分かるし。これのおかげで、ツールのラッパーを作るときにちょっと怠けられるっていうのもある(スタートアップが1msなら何千回でも問題ないけど、40msだと問題になる)。特定のPythonのバージョンやパッケージをユーザーが持ってるか気にしなくていいから、配布もずっと楽になるよ。

私は常に合計型、パターンマッチング、非同期サポートがある言語を選ぶよ。コンパイル時にエラーをキャッチできるのも大きな利点だし。Rustである必要はないけど、その条件を満たしたら、なんでRustじゃダメなの?

これは基本的にカゴ・カルトだね。50年前にGCベースの言語で作られたグラフィカルワークステーションがたくさんある。 - Interlisp => https://interlisp.org/ - Cedar => https://www.youtube.com/watch?v=z_dt7NG38V4 もし、ほとんどがElectronのクソや1970年代のCLIツールを動かしているハードウェアで、今何ができたか想像してみて。

この意見にはあまり同意できないな。いくつかの疑問点を挙げると、「認知的負荷:簡単なタスクでも、ライフタイムや所有権、借用スコープについて常に考えなければならない。私のノートツールのような小さなCLIが急に熱いポテトを juggling しているように感じる。」これがCやZigを使っても消えないよ。ただコンパイラからの助けが少なくなるだけ。「開発者は馬鹿じゃない」疲れていたり気が散っていたりすると、賢い人でも間違いを犯すことがある。馬鹿でないことは、自分の誤りを認識してそれに対処しようとすることだ。私が言いたいのは、投稿が触れていない点だけど、Rustコンパイラが安全なプログラムのサブセットを推論する能力は現在十分ではなく、あまりにも頻繁に完全に良いプログラムを拒否してしまう。良い例として、次のようなコードが実際には問題ないことを表現できないことがある:struct Foo { bar: String, baz: String, } impl Foo { fn barify(&mut self) -> &mut String { self.bar.push_str("!"); &mut self.bar } fn bazify(&self) -> &str { &self.baz } } fn main() { let mut foo = Foo { bar: "hello".to_owned(), baz: "wordl".to_owned(), }; let s = foo.barify(); let a = foo.bazify(); s.push_str("!!"); } これが、fn barify(bar: &mut String) -> &mut String { bar.push_str("!"); bar } // main内で let s = barify(&mut foo.bar); のような awkward な構造を生むんだ。

君のコードを見て、引用された発言が間違ってるって自信が持てたよ。

これは素晴らしい例ですね。もう少し詳しく見てもいいですか?それを私の記事に使いたいかもしれません。

あなたに反論しますが、誤検知を避けるために(プログラマーが正しくても、コンパイルは失敗する)コードを2番目や3番目に良い設計にリファクタリングするのは、文句を言う価値のある認知的負担そのものです。これが全体のコードベースの設計を根本的に変える可能性があります。これが、非常に複雑な仕事をする多くのゲーム開発者が、Rustのトレードオフを価値がないと見なす理由だと思います。システム設計の選択肢が減ると、すでに難しいタスクがさらに難しくなります。Rustコンパイラが誤検知を全く出さなければ、理論的には(構文的/意味的な欠陥を無視すれば)ほぼすべてのものと同じくらい使いやすいはずです。言うは易し、行うは難しですが。

彼の言ってることには一理あるね。Rustのバックリンクは難しすぎる。Rc、Weak、RefCell、.borrow()を使えば安全にできるけど、簡単じゃない。プログラムが短時間で終了するなら、アリーナ編集も選択肢だね。著者が「CLIツール」と言ってるのはそういうことだと思う。ライフタイムの問題であって、入力フォーマットの問題じゃない。「Rustは素晴らしい、大規模でマルチスレッドの長寿命なものを作るなら、コンパイル時の保証が本当に役立つ。借用チェッカー、ライフタイム、所有権のルールは大規模システムにとって恩恵だ。」そうだね。それがRustの本来の目的だよ。私はRustで大規模なメタバースクライアントを書いたけど、実行する回帰テストの一つはアバターをツアービークルに乗せて24時間走らせることなんだ。約20スレッドで、メモリリークもクラッシュもなし。C++ではQAチームやValgrindなどの外部ツールが必要になるし、解釈系言語では遅すぎるよ。

私は地球の曲率や局所的な重力の変動を考慮した物理的に正確なフライトシミュレーターをRustで書いたんだ。それには2つのテストがあって、一つはSFOからサクラメント、シアトルまで約5時間の「フライト」を60フレーム/秒で飛ぶこと、もう一つはSFOから東京まで約22時間のフライトだ。これまで一度もクラッシュしたこともメモリリークもなく、常に完璧に動いてる。Rustを書いて7年になるけど、クラッシュはほんの数回、主に自分の2Dソフトウェアレンダラーを書いてるときだけ。最近CやC++に触れるのはレガシーの修正だけだよ。

Zigは開発者に優しい設計で、少しの規律があればメモリ安全なソフトウェアを作れる。一方でCも少しの規律があればメモリ安全なソフトウェアを作れる。この「少しの規律」が問題なんだよね、開発者がそれを欠いている。

50年も経って失敗してるんだから、少林寺の修行より難しいに違いない。

ここにはレベルがあって、Zigは以下のことに対応してる: - 範囲外アクセス(CVEsの70%) - nullptr参照 - 型安全の問題 これはCよりも圧倒的に良いよ。解放後の使用エラーを防ぐのは、境界を見逃したり符号付き/符号なし変換を失敗するよりもずっと少ない規律で済む。Zigは素晴らしいクロスプラットフォームのビルドシステムを持っていて、コンパイル時に素晴らしい最適化や使いやすさを実現してるし、C++やRustよりも桁違いにビルド時間が速い。Zigはまだv1.0未満だから、標準ライブラリには改善の余地があるし、他にも問題はあるけど、将来的にはパフォーマンス重視のプログラムにとって素晴らしい選択肢になると思う。