※本記事は、Andrew Kelley氏によるライブストリーム「Zig Roadmap 2026」の内容を基に作成されています。Andrew Kelley氏はZigプログラミング言語の創始者であり、Zig Software Foundation(ZSF)の代表を務めています。動画はYouTubeにて公開されており、タイムスタンプ付きの目次(イントロ・パッケージマネージャー・x86バックエンド・zig cc・ラベル付きswitch・新ターゲット・ファイルシステム監視・インクリメンタルコンパイル・translate-c・AArch64バックエンド・非同期IO・ネイティブファジング・コミュニティアナウンス・Q&A)に沿って進行しています。本記事では動画の内容を日本語で要約・解説しております。なお、本記事の内容は原著作者の見解を正確に反映するよう努めていますが、要約や解釈による誤りがある可能性もありますので、正確な情報や文脈については、オリジナルの動画をご視聴いただくことをお勧めいたします。
1. イントロダクション
1.1 配信の趣旨:過去1年の振り返りと今後のロードマップの概要
Andrew: みなさん、こんにちは。久しぶりですね。このストリームは、Zigをまだ使っていない方や、最近チェックしていなかった方に向けて、「この一年で何があったのか」「あの人たちは次に何をしようとしているのか」をお伝えするためのものです。リリースノートにも多くの情報を載せていますので、テキストで読みたい方はそちらをご覧ください。ただ、動画派の方もいますよね。このストリームはそういう方のためにあります。
本題に入る前に、まずLorisにひとつ確認させてください。アナウンスを先にすべきでしょうか、それとも予定していたアジェンダに直接入っていいでしょうか。
1.2 Software You Can Love Vancouver 2026 の速報アナウンス
Andrew: 答えを待ちながら自分で予想してみると、おそらくSoftware You Can Love Vancouver 2026のアナウンスをすべきだろうと思います。Matt Knightが関わっているはずですが、ウェブサイトはまだ2023年のままです。ただ、これは正式なアナウンスです。2026年にVancouverでSoftware You Can Loveを開催します。日程はまだ決まっていませんが、Matt Knightがすでに準備を進めてくれています。本当にありがとう、Matt。とても素晴らしいイベントになるはずです。ぜひ頭の片隅に入れておいてください。それでは、本題に入っていきましょう。
2. パッケージマネージャー(package manager)
2.1 zig fetch --save の新ワークフロー:Gitプロトコルによる依存解決・コミットへのロック・ハッシュ自動算出
Andrew: 最近力を入れて取り組んできたことのひとつが、パッケージマネージャーです。すでに使っている方もいると思いますが、私が今遊んでいる個人プロジェクトを例にお見せしましょう。即時モードのUIをいじっていて、フォントレンダリングやVulkanを使っています。いずれはこれを音楽系のツールにしたいと思っているのですが、今日お伝えしたいのはその話ではなく、最近追加したワークフローについてです。
依存関係を見てみると、Mason Romeuが作ったシェーダーコンパイラへの依存が一つあります。これはGLSLをSPIR-Vにコンパイルするパッケージです。将来的にはZigバックエンドを用意する予定で、Ali ChughtaiとRobin Verdureのおふたりがすでにプルーフオブコンセプトを作ってくれています。しかし今のところはこのシェーダーコンパイラを使っています。そしてこれを更新したい、というのが今日のユースケースです。
従来であれば依存関係の更新は少し手間のかかる作業でしたが、今は非常に簡単なワークフローが使えるようになりました。たとえば最新のmasterに追従したい場合、zig fetch --save に + とGitHubのリポジトリURLを渡すだけです。するとmasterブランチそのものに依存するのではなく、その時点の特定のコミットに解決してくれます。そしてそのコミットをzig.zonファイルに書き込み、ハッシュも自動的に計算してシステムにインストールしてくれます。Gitプロトコルを使ってフェッチし、特定のコミットにロックして、ハッシュを算出するところまで、コマンド一発で完結するわけです。
zig fetch --save をまだ使ったことがない方は、ぜひ試してみてください。非常に便利なワークフローです。
3. Zig ネイティブ x86 バックエンド vs LLVM
3.1 自作x86バックエンドがデバッグモードのデフォルトに昇格した経緯と実測デモ
Andrew: 次にお伝えしたいのは、x86バックエンドがデバッグモードのデフォルトになったという話です。devlogの投稿を追っていない方のために説明すると、Jacob YoungとMatthew Luggを中心とした取り組みによって、LLVMに依存しない自作のx86バックエンドが64ビット環境で十分な堅牢性を持つに至り、ついにデフォルトとして採用されました。0.15のリリース、暫定日程では8月1日のリリースでこの変更がデビューします。
実際にデモをお見せしましょう。まずビヘイビアテストをLLVMありで実行すると、1,000件のテストが走ります。次にLLVMフラグを外して同じテストを実行すると、x86マシン上ではデフォルトで自作バックエンドが使われます。結果を見ると、実行が大幅に速くなっているだけでなく、パスしているテスト数が増えてスキップが減っています。つまりカバレッジもLLVMより若干広いということになります。もちろんすべての面で厳密に優れているわけではありませんが、おおむね自作バックエンドのほうが優秀だと言えます。
3.2 LLVMとの比較:コンパイル速度・メモリ消費・テスト通過数
Andrew: コンパイル速度についても実測してみましょう。Hello Worldプログラムを使って、LLVMありとLLVMなしでそれぞれ約5秒分のデータを取ってみます。結果を見ると、LLVMを使った場合は1秒かかっていたのに対し、ZigのバックエンドではLLVMをバイパスすることで225ミリ秒まで下がりました。メモリ使用量も削減されています。この名前、CLIツールの名称はストリーム上で決めたのですが、Matt Knightとembedded boyに感謝です。
なお、このバックエンドはビヘイビアテストだけに適用されるわけではありません。build.zigの実行にも使われますので、x86環境であればビルドコマンド自体も速くなります。
3.3 Cライブラリとの連携・build.zigへの適用・Windowsにおける制限とリリーススケジュール
Andrew: よくある質問として「Cライブラリをリンクしていてもこのバックエンドは使えますか」というものがあります。答えはイエスです。CのコードをリンクしていてもZigの自作バックエンドは問題なく動作します。実際、先ほどデモしたVulcanのCライブラリを使っているプロジェクト自体が、自作バックエンドでビルドされています。
一方、Casey Muratoraが指摘してくれた重要な注意点があります。Windowsではまだx86バックエンドはデフォルトになっていません。これはリンカーの強化がいくつか必要なためです。動作はしますが、デフォルトとして有効化するにはCoffリンカーに手を入れる必要があり、0.15に間に合うかどうかは微妙なところです。ただ優先度の高い課題として取り組んでいます。
また、Macコンピューターをお使いの方は「自分はどうなるの?」と思っているかもしれません。その話は後ほど出てきますので、少々お待ちください。
4. zig cc と UBSan トレース(zig cc ubsan traces)
4.1 zigcc の設計思想:Clangとの違いとフラグ変換ロジック
Andrew: 次は、多くの方がZigを知るきっかけになっているzig ccについてお話しします。zig ccはCコンパイラのコマンドラインインターフェースと互換性を持ちつつも、Clangそのものを目指しているわけではありません。特にデフォルト設定が異なります。このデフォルトはZigのパラダイムに対応したものになっています。
仕組みとして重要なのは、zig ccがCのコマンドライン引数を受け取り、それをZigのコマンドライン引数に相当するものへ変換し、さらにそれをClangへ渡すコマンドへと落とし込むという中間変換のステップを持っていることです。これは一見遠回りに見えますが、この中間状態が非常に重要な役割を果たします。
具体的な例を挙げましょう。-O2 を渡すと、zig ccはこれをrelease fastとして解釈します。一方、-O2 に加えて -fsanitize でUBSanを有効にすると、この組み合わせをrelease safeとして解釈し、後でそれらのフラグに落とし込みます。なぜこの中間状態を経由するかというと、その状態が他のあらゆる設定に波及するからです。たとえばデフォルトはデバッグモードであり、デバッグモードでは自動的に未定義動作サニタイザーが有効になります。
4.2 UBSanライブラリの追加によるバックトレース付きパニックメッセージの実演
Andrew: デバッグモードではデフォルトでUBSanが有効になっていたのですが、今回David Rubinの貢献によって、さらにUBSanライブラリがデフォルトで追加されるようになりました。これによって未定義動作が発生したときに、何が問題なのかを説明するメッセージとバックトレースが表示されるようになっています。
実際にデモをお見せしましょう。チャットで「好きな未定義動作を叫んで」とお願いしたところ、gray headerが符号付き整数オーバーフローを挙げてくれました。では int 型の引数を受け取って arg + (2の31乗 - 1) を計算するコードを書きます。これをzig ccでコンパイルしてみましょう。初回ビルドではlibubsanのビルドに少し時間がかかりますが、これはターゲットごとに一度だけ行われるものです。Zigのバージョンを切り替えない限り、次回以降は即座に再ビルドされます。
実行してみると、意図していた未定義動作に到達する前に別の未定義動作を踏んでしまいました。Cというのはそういうものですね。フォーマット指定子が int 型を要求しているのに引数が long 型になっていたり、-1 が必要なところを +1 にしていたりと、修正を重ねてようやく意図した符号付き整数オーバーフローを発生させることができました。実行すると未定義動作が検出され、問題を説明するメッセージとバックトレースがきれいに表示されます。
4.3 気づきと考察:zigcc はC言語学習環境として優れているという見解
Andrew: デフォルトでUBSanが有効になっていたこと自体は以前からそうでした。今回の新しい点は、何が問題なのかを教えてくれるUBSanライブラリがデフォルトで追加されたことです。これはC言語を学んでいる初心者にとって非常に親切な環境だと思います。
私はzig ccがC言語を学ぶ学生にとって非常に優れた環境になり得ると確信しています。最適化を有効にしたら突然動かなくなった、実行時に謎の誤動作が起きるといった問題に学習者が詰まるケースが大幅に減るからです。この仕組みはZigの標準ライブラリの中でpanicを呼び出し、Zigの標準ライブラリがスタックトレースを処理することで実現しています。シンプルでありながら非常に実用的なアプローチだと思います。
5. ラベル付きswitch(labeled switch)
5.1 構文の概要とZigトークナイザーへの適用例
Andrew: 次は言語の変更点の中から、私が特に気に入っている新機能をご紹介します。ラベル付きswitchです。これを説明するにあたって、Zigのトークナイザーを例に使います。コンパイラ開発に詳しくない方のために説明すると、トークナイザーはコンパイラが最初にソースコードに対して行う処理で、どこがスペースでどこがキーワードでどこが句読点かを識別するものです。Zigのトークナイザーは1,776行あり、基本的にはステートマシンとして実装されています。ほぼすべてのトークナイザーはステートマシンです。
トークナイザーの構造を見ると、取りうる状態を表すenumがあり、その心臓部は大きなループの中でステートと次のバイトをswitchしているというシンプルなものです。ここでラベル付きswitchが登場します。Zigにはgotoがありません。実はZigはかつてgotoを持っていましたが、削除しました。gotoの原則そのものに反対だったわけではなく、ラベル付きbreak・ラベル付きcontinue、そして今回のラベル付きswitch continueと重複していることがわかったからです。
このラベル付きswitchでは、switch文にラベルを付けることでループのようにcontinueできるようになります。面白いのは、このswitchが定数値でswitchしているという点です。ステートを変数に保存することすらしていません。ステート変数が存在せず、制御フローが直接該当のcaseへ飛びます。そして各ブロックの終わりにラベル付きcontinueを行うことで、異なる値でswitchの先頭に戻ります。
5.2 実験結果:ブランチプレディクターへの効果・13%高速化の実測・コード可読性の向上
Andrew: このcontinueに渡す値がコンパイル時定数であれば、それは単純なgotoになります。たとえば文字列リテラルのenum値でcontinueすると、直接そのcaseへジャンプします。これは構造化されたgotoであり、変数の初期化を飛ばしてしまうような危険なgotoではありません。コンパイル時定数でないランタイムの値を使うことも可能です。その場合でも、分岐ロジックがその場所で処理され、直接次の目的地へジャンプするという動作は変わりません。
これがブランチプレディクターに対して非常に効果的です。whileループの中でswitchしている場合、そのswitchはミス予測されやすくなります。ソースコードの次のバイトが前のバイトと同じである可能性は低いからです。しかしラベル付きswitchのパターンでは、すべての分岐がそれぞれのcase内部に移動し、次に行くべき場所へ直接ジャンプします。コンパイル時定数の場合はそもそも予測すら不要で、単純なジャンプになります。ランタイム値であっても、同じ分岐が繰り返し予測されやすい状況が生まれるため、ブランチプレディクターの命中率が大幅に上がります。
実際にZigのトークナイザーをこのパターンに切り替えたところ、13%の速度向上が報告されました。パフォーマンスだけでなく、コードの読みやすさという観点でも優れていると思います。whileループで書いた場合、ある処理がどこへ飛ぶのかを把握するためにループの先頭まで戻って確認する必要がありますが、ラベル付きswitchであれば大きなステートマシンの途中を読んでいるときでも、このcontinueがどのcaseへ飛ぶのかが一目でわかります。パフォーマンスと可読性を同時に改善できるこの機能は、本当に気に入っています。
6. 新しいターゲットサポート(new targets)
6.1 追加ターゲット:LoongArch・S390X・FreeBSD・NetBSDとCI自動化
Andrew: 次はツールチェーンの改善についてお話しします。これらはZigの最新コアチームメンバーであるAlex Rønne Petersenの貢献によるものです。彼はターゲットサポートを大幅に拡充してくれました。気づいていない方もいるかもしれませんが、Zigのダウンロードページがかなり大きくなっています。もはや画面一つに収まらないほどです。
Windows x86_64・ARM64・32ビット、各種Linux向けに加えて、LoongArchとS390Xが新たに追加されました。さらにAlexはgibcとmuslのlibc戦略がどのように機能しているかを学んだうえで、それをFreeBSDとNetBSDに適用してくれました。その結果、これらのプラットフォーム向けのビルドもCIで自動化されています。毎日おおよそ、これらのオペレーティングシステムを使っている方であれば最新のZigビルドが手に入るようになっています。しかもlibC以外の依存関係は一切不要です。ダウンロードして展開するだけで、すぐに使い始めることができます。
6.2 クロスコンパイルの実演とlibc更新手順のWiki整備
Andrew: Zigはもともとこれらのターゲット向けにクロスコンパイルする能力を持っていましたが、今回の変更でヘッダーも提供されるようになっています。実際に試してみましょう。少し変わったターゲットとして、64ビットビッグエンディアンのPowerPC FreeBSD向けにHello Worldをビルドしてみます。zig cc hello.c に対してターゲットとして powerpc64-freebsd を指定するだけです。UBSanも有効になっています。今回のデモ中にlib Cまわりで小さなリグレッションが見つかりましたが、リリース前には修正される問題です。結果として、FreeBSD 14の最新リリース向けに64ビットビッグエンディアンPowerPCのHello Worldバイナリが生成されました。このマシンでは実行できませんが、file コマンドで確認するとちゃんとそのアーキテクチャ向けのバイナリであることがわかります。
またAlexはlibCのツールチェーンを最新の状態に保つための手順をWikiページとしてきちんと整備してくれました。glibc更新時の手順、musl更新時の手順、そして今回新たにFreeBSD向けとNetBSD向けのセクションも追加されています。誰が担当しても同じ手順で再現できるプロセスを文書化してくれたことは、チームにとって非常に価値のある貢献です。ターゲットサポートの拡充とその維持管理体制の整備、両面において素晴らしい仕事をしてくれました。
7. ファイルシステム監視とインクリメンタルコンパイル(file system watching / incremental)
7.1 -watch フラグと即時フィードバックワークフローのデモ
Andrew: 次はファイルシステム監視についてお話しします。まだZigを最近チェックしていなかった方は、ここは特に注目してください。私が今取り組んでいるブランチでは大量のコンパイルエラーを処理する必要があり、そのワークフローをお見せしたいと思います。
ビルドコマンドに渡しているオプションを説明します。まずバイナリの出力を無効にしています。コンパイルエラーだけを確認したいので、実行ファイルを生成する必要はありません。そしてビルドシステムに対して生き続けるよう指示し、入力ファイルに変更があれば自動的に再ビルドするよう伝えています。さらにインクリメンタルコンパイルを有効にして、コンパイルエラーを画面の下部に表示するオプションも加えています。
これを実行するとコンパイラ全体をゼロからビルドし始めます。compiler RTを除いてキャッシュは一切使っていません。約50万行のコードを完全に最初からビルドして、15秒で完了しました。先ほどデモした自作x86バックエンドを使っています。ビルドが終わったら編集を始めてみましょう。たとえば @breakpoint というビルトインを削除してみます。これは良くないビルトインだと思っているので実際に消してみます。ファイルを保存した瞬間にエラーが表示されます。そのエラーを見ながら次のファイルを修正して保存すると、また即座に再ビルドが走ります。変更の規模によって数秒かかるものもありますが、多くのケースでは瞬時にフィードバックが返ってきます。
今お見せしているのは、バイナリ出力の無効化・ファイル変更の自動監視・インクリメンタルコンパイル・コンパイルエラーの下部表示、この四つの組み合わせです。このワークフローを使うと、コードを書きながら即時でコンパイルエラーのフィードバックが得られます。これは0.14から使えますが、次のリリースでさらに改善されています。またZLSもこの機能を活用しているので、エディタ上でスクイグル表示としてエラーを確認することもできます。
このワークフローをまだ使っていない方には強くお勧めします。即時フィードバックが得られると、ちょっとした待ち時間にFirefoxを開いてSNSを見てしまうような誘惑がなくなります。集中力を保ちやすくなるのです。
7.2 バイナリ生成を伴うインクリメンタルコンパイルが未完成な理由と今後の方針
Andrew: 先ほどのデモではバイナリ出力を無効にしていましたが、理想的にはバイナリを生成しながらもインクリメンタルにリビルドして即座にテストできるようにしたいところです。これは実験的には動作しています。試しに自分の音楽プロジェクトで有効にしてみましょう。Vulkanのセットアップが必要だったりシェーダーコンパイラを更新した関係でリビルドに時間がかかっていますが、ファイルを変更して保存すると監視が機能していること自体は確認できます。
ただし問題があります。変更を加えて保存した後、ビルドは成功と報告するのに生成されたバイナリが正常に動作しません。リンカーがミスコンパイルを起こしています。これが現状の問題点です。フロントエンドはうまく動いているのですが、バイナリ生成を伴うインクリメンタルコンパイルを完全に動作させるには、リンカーの強化がまだ必要です。ELFリンカーとCoffリンカーの両方に手を入れる必要があります。Windowsでx86バックエンドをデフォルトにするためにも同じリンカー強化が必要なので、これは複数の課題を解決する重要な作業です。
7.3 macOSにおける監視機能の制限とFSEventsStream対応の計画
Andrew: バグの話もしておきましょう。つい先週まで、Linuxで同じファイルシステムエラーを引き起こすバグがありましたが、これは完全に修正されています。一方macOSにはまだ問題が残っています。macOSのファイル監視プリミティブはあまり良くありません。LinuxはFA notifyというAPIが提供されており、特にカーネル5.1以降はかなり使いやすいものになっています。しかしmacOSのシスコールだけを使って最善を尽くした結果、ファイルをアトミックに更新するエディタでは変更を検出できますが、単純に書き込みを行うエディタでは更新を検出できないという問題があります。
解決策としては、macOSのfs event streamというフレームワークを使う必要があります。これはユーザースペースでファイルシステム全体を監視するようなもので、そのデータベースをクエリする形で変更を検出します。ビルドシステムの中でこれをdl openして使えるようにすれば問題が解決するはずです。ただしこれはまだ実装されていないので、macOSのファイル監視機能には現在大きなアスタリスクが付いている状態です。
7.4 キャッシュ肥大問題の原因と状態シリアライゼーション完成による根本解決の計画
Andrew: インクリメンタルコンパイルを使っているとZigのキャッシュサイズが大きくなるという問題を指摘してくれた方がいました。これはもっともな指摘です。インクリメンタルコンパイルの設計上、既存の出力を最小限の変更で新しい状態に更新するのが本来の動作です。編集をすると、コンパイラがその編集に対応した最小限の変更を出力に加えます。これ自体は大量のキャッシュゴミを生むものではありません。
しかし現状では、インクリメンタルコンパイルの状態保存がまだ堅牢に実装されていないため、デフォルトのキャッシュモードでは毎回新しいアーティファクトを生成してしまっています。これがキャッシュに大量のゴミを積み上げてしまう原因です。この問題に対して、キャッシュのサイズ上限をいじって対処しようとは思っていません。根本的な解決策は状態のシリアライゼーションを完成させることです。それが完成すれば、インクリメンタルモードを使ったときに毎回新しいキャッシュエントリを作るのではなく、既存のバイナリをそのまま上書き更新するというデフォルト動作に戻れます。同じファイルを繰り返し更新するだけなので、ゴミが積み上がることもなくなります。
8. 新しいtranslate-c とAArch64バックエンド(new translate-c / aarch64 backend)
8.1 AROベースのtranslate-cパッケージ完成とClang・LLVM依存排除へのロードマップ
Andrew: 次はtranslate-cの話をします。しばらくの間、静かに水面下でVeaがZig向けのtranslate-cパッケージを開発していました。これはAROと呼ばれる彼自身のCコンパイラをベースにしたもので、純粋なZigで書かれています。そしてこれが完成しました。既存のtranslate-cテストスイートと完全な互換性を持っており、すぐに使える状態です。
現状のtranslate-cがどう動くかをお見せしましょう。zig translate-c を実行すると、CのコードがZigに変換されたテキストが出力されます。Cが定義する大量のマクロも含まれますが、私たちのmain関数もちゃんと変換されています。これはClangをベースにしており、Clangが内部でCコードを解析してZigへの変換を行います。Veaが実装したのはClangを完全にバイパスするものです。彼のCコンパイラARO上で動作するので、純粋なZigだけでCコードをZigに変換できます。
このパッケージをまだ手元のプロジェクトに統合していないので今日デモするには数分かかりますが、次のステップとしてはこのコマンドラインを実行したときにClangをまったく使わず、ARO libcだけを使う形に切り替えることになります。さらに面白いのは、このtranslate-cをバックエンドに接続することで、Clangを一切リンクしないzigccを実装できるかもしれないという点です。AROで変換してZigを通して出力するというパイプラインです。これはいつかフォローアップストリームでやってみたいと思っています。
重要なのはその方向性です。私たちはClang依存の排除に向けて着実に進んでいます。LLDへの依存排除も進んでいます。そしてLLVM全体への依存排除も進んでいます。これがAArch64バックエンドの話への自然な流れになります。
8.2 Jacob LyによるARM64自作バックエンドの公開・テスト結果・独自アーキテクチャと最適化の観察
Andrew: さて、ここで秘密を明かす時が来ました。Jacob Lyがずっと私たちを焦らし続けてきた極秘プロジェクトについて、ついに彼から教えてもらいましたので、ストリーム上でデモをお見せします。その秘密とは、ARM64バックエンドです。
実際に試してみましょう。まずコンパイラをソースからビルドします。ビルドが完了したら、このコンパイラでARMのビヘイビアテストを実行します。いくつかフラグを追加する必要があります。ターゲットをARM64 Linuxに指定し、LLVMバックエンドを使わないよう指示します。またZigのデバッグモードは安全性チェックなど多くの機能を持っていますが、--really-small を渡すことでバックエンドが処理すべきタスクを最小限に絞ります。compiler RTもまだサポートされていないので無効にします。そしてQEMUを使ってテストを実行します。
結果を見ると、154テストがパスして129がスキップされています。過半数のビヘイビアテストが通っています。ただし興奮しすぎる前に注意が必要で、これらのテストの多くはフロントエンドをテストするものなので、最初の一つが通ればまとめて通るケースが多くあります。とはいえ、確かな進歩です。
Jacobはこのバックエンドの設計についていくつか共有してくれました。コードを見ればわかりますが、これは新鮮なアプローチで書かれています。独自のデータ構造を採用しており、異なるアイデアを試すために既に数回リライトを行っています。彼が見せてくれたデモを通じて、このバックエンドの最適化の一端を垣間見ることができます。4つの引数を受け取って1つ左にシフトして渡す関数と、1つ右にシフトして渡す関数のコード生成を比較すると、前者はレジスタを0・1・2・3の順に移動させて次の関数を呼び出しますが、後者は3・2・1・0と逆順に処理することで、わずか4つのレジスタだけを使って呼び出しを実現しています。もう一方は数学的に一時レジスタの導入が必要になるため4つのレジスタに加えて一時レジスタが使われます。これはデバッグバックエンドであるにもかかわらず、すでにこういった細かな最適化が入っているのです。
Jacobに怒られるかもしれませんが、私たちのゴールはコンパイル速度だけでなく、デバッグモードにおける機械語の品質でもLLVMを超えることだと思っています。Jacobの取り組みを見ていると、それが可能に思えてきます。
9. IOインターフェースとしての非同期処理(I/O as an interface — async/await)
9.1 設計思想:IOをアロケーターと同様にパラメータとして渡す統一インターフェースへの転換
Andrew: 次は私自身が取り組んでいることをお見せします。async/awaitの復活です。ただしそれ以上のものです。数週間前にアムステルダムでこのテーマについて講演を行いました。録画が公開され次第、私のウェブサイトに掲載します。まず前置きとして言っておきたいのは、これは理論的には解決できたと感じているということです。以前のasync/awaitは完成した感じがしませんでした。良い出来には思えませんでした。しかし今回の新しいアプローチには、私のビジョンを実現できる道筋があると感じています。
アイデアそのものはシンプルです。シンプルなアイデアほど思いつくのが難しいものですが。Zigプログラムではアロケーターをあちこちに渡しますよね。これと同じように、今後はIOも渡すことになります。IOとは何かというと、ほぼすべてです。asyncもIOです。awaitもIOです。これはGoプログラミング言語へのオマージュでもあります。Goはasyncキーワードなしのawaitのような、言わば不完全なasync/awaitです。キャンセルもあります。ファイルを開くこと、ネットワーク通信、mutex、条件変数、タイマー、これらすべてがIOです。現在のスレッドの実行をブロックし得るもの、大きなCPUタスクも含めて、すべてここに属します。
つまりZigプロジェクトではmainの中で最初にやることの一つとして、アロケーターを選ぶのと同じように、IO実装を選ぶことになります。スレッドプールベースの実装を選んでそこからIOを取得する、あるいはグリーンスレッドのイベントループベースの実装を選んでそこからIOを取得する、どちらでも構いません。この二つをコード上で入れ替えても、プログラムの出力は同じになります。挙動は違いますが、出力は同じです。そしてその上でasyncが使えるようになります。asyncとawaitのキーワードが将来的に削除されれば、これらは通常の関数呼び出しになります。
9.2 IOが包含するもの・IOImplementationの選択によって同一コードの挙動が変わる仕組みのデモ
Andrew: このアプローチの要点はロジックに並列性を埋め込めることです。たとえば前半の計算と後半の計算を開始しておき、後でその結果が必要になったときに初めて取得するという形で書けます。計算を開始して、待っている間に別の処理をして、後で結果を取得する。このロジックをコードとして表現できます。そしてこのコードを使う側、つまり別の人がどのIO実装を選ぶかによって、そのロジックがどのように実行されるかが決まります。ちょうどアロケーターを関数に渡してメモリの確保方法を呼び出し側が決めるのと全く同じ構造です。
この設計には数多くのメリットがあります。まずリソースリークチェックが可能になります。テスタビリティの向上も大きいです。コードをイベントループ用と非イベントループ用に二重実装する必要がなくなります。そして「自分でOSを持ち込む」という点も重要です。以前はOS依存のパッケージがあると使えないOSが出てきましたが、IOインターフェースへの依存に変えることで、自分のホビーOSにこのインターフェースさえ実装すれば、そのパッケージがすべて動くようになります。誰も意識せずに済む形です。
さらに、これがユーザーランドにあってキーワードではないため、キャンセルやselectのような便利機能の実装が格段に簡単になります。実際にGoのchannelに相当するQと、selectをユーザーランドで実装しました。これは今チェックアウトしているブランチで実際に動作しています。selectは複数のasyncを渡して最初に完了したものを実行する機能です。キャンセルと組み合わさっているのも面白い点で、三つの処理を開始してそれぞれにdefer cancelを付けておくと、この関数からreturnする際にすべてのリソースが自動的にクリーンアップされます。Zigはガベージコレクション言語ではないにもかかわらず、たとえばGoと比べてもボイラープレートが少ない。tryの存在もあってGoより短くなる場合もあります。
9.3 シングルコンパイルユニット戦略・制限付き関数ポインタ型によるスタック上限計算の技術的背景
Andrew: なぜシングルコンパイルユニット戦略がこのようなasyncを可能にするのかという質問がありました。鍵はスタック使用量の問題にあります。二つのことを並列に行うとき、もう一方のスタックをヒープに確保することになります。vtable、つまりアロケーターやIOのような再利用可能なインターフェースを使う場合、ランタイムにどの関数が呼ばれるかが決まる関数を持つことになります。問題は、その関数のスタック使用量の上限を知る必要があるのに、どの関数が呼ばれるかがランタイムまでわからないという点です。再帰の問題も別途あります。
これを解決するのが制限付き関数ポインタ型(restricted function pointer types)という提案です。コンパイラが自動的に、あるfunction pointerが限られた集合のうちのどれかにしかなり得ないと判断できるようにするものです。それによってスタック使用量の上限をその関数群の最大値として計算できるようになります。これで並列処理に必要なすべての関数呼び出しスタックをあらかじめ確保できます。もし一つのコンパイルユニットに全部が入っていなければ、function pointerが指し得る関数の閉じた集合を知ることができないため、この提案は実現できません。シングルコンパイルユニット戦略とインクリメンタルコンパイルへの多大な投資こそが、このアプローチを前進させているのです。
9.4 キャンセル・select・Qチャネルの実装と std.io.Reader/Writer の非ジェネリック化への移行
Andrew: 現在このブランチのマージに時間がかかっている理由は、std.io.ReaderとWriterを新しいパターンとasync/awaitのパターンに合わせて変更しているからです。これは残念ながらかなり大きな変更です。
現在のWriterはジェネリックに依存しています。新しいWriterはジェネリックではありません。これは単なる構造体で、いくつかのフィールドを持ち、このインターフェースを満たすには特定の関数を提供する必要があります。オプションのデフォルト付き関数もあります。それを満たすとフォーマット関連のメソッドなどが無料でついてきます。良い点はこのファイル内のコードがジェネリックでないことです。つまり同じ機械語がすべてのWriterで使い回されます。フォーマット処理コードが大量にありますが、その同じ機械語があらゆるストリームで使われます。
ではsyncとasyncで別々のReader/Writerが存在するのかという疑問が出てきますが、そうではありません。IO実装の選択によって決まります。シングルスレッドブロッキングのIO実装を選べばブロッキングな動作になります。たとえば圧縮ストリームであればバイトを渡してその変換結果が出てくるという純粋な関数に近い形で、OSやIOをまったく知らずに済みます。一方でファイルへの書き込みのような処理はchainの末端にあるため、IO実装が必要です。IO実装に対してwriteVや相当する処理を呼び出し、それがdrainをsyncにするかasyncにするかを決定します。制限付き関数ポインタ型がすべてsyncであればsyncにまとめ、asyncが混在すればfunction pointerをasyncにします。スタックレスコルーチンがなくてもスレッドプール、シングルスレッドブロッキング、グリーンスレッドの実装だけで十分に有用です。後からスタックレスコルーチンを追加することで、これまで不可能だったユースケースが開けてきます。
9.5 スタックレスコルーチンの位置づけの変更・関数カラーリング問題への応答・async/awaitの設計哲学
Andrew: スタックレスコルーチンについても触れておきましょう。将来的にはおそらく必要になると思っています。たとえばユーザースペースでyieldを実装する手段を持たないターゲットでは、スタックレスコルーチンによってそれを実現する必要があります。しかしその位置づけは大きく変わります。ユーザーが触れる機能ではなく、IO実装の実装詳細になるのです。再利用可能なパッケージを書いている人ですら気にしなくて良い、標準ライブラリ作者や独自IO実装を作る高度なユーザーだけが意識するプリミティブになります。
関数カラーリングの問題についても質問がありました。アロケーターを受け取ることも一種の関数カラーリングと見なせます。fooを呼びたいのにアロケーターがないと呼べない、だから持たなければならない。これは確かにカラーリングの一形態かもしれません。しかし私にとってこれは許容できる形です。問題ないと思っています。私が避けたかったのは、Rustのasync-stdのように同期版と非同期版で二つの標準ライブラリを持たなければならない状況です。一つの標準ライブラリで、イベントループのコンテキストでもシングルスレッドのコンテキストでもWebAssembly上でも、どこでも動くものが欲しい。関数カラーリングかどうかはもはや学術的な議論になってきます。本質は再利用可能なコードです。同じ実装がすべてのユースケースを満たせているなら、技術的にカラーリングかどうかは重要ではありません。
最後に正直に言っておくと、これはasync/awaitへの四度目の挑戦です。以前の実装は完成した感じがしませんでした。何度も試して他のアプローチをすべて試してきた結果、今回ようやくこれが正しい方向だという確信があります。大幅な破壊的変更を伴うことは申し訳なく思っています。std.io.ReaderとWriterを変更するとそれを触るすべてのコードを書き直す必要が生じ、本当に大変な作業です。しかしこれが未来だと強く確信しています。私が唯一テーブルに持ち込めるものは、完璧でないものに妥協しないということです。それが破壊的変更という代償を生んでいます。本当に申し訳ありませんが、信じて待っていてください。
10. ネイティブファジング(native fuzzing)
10.1 内製ファジングツールチェーンの開発方針と現状実装のデモ
Andrew: 次はファジングについてお話しします。正直なところ、最近はこの作業にあまり時間を割けていません。しかしロードマップの中でも確実に取り組む予定の項目です。きっかけはLorisがAFLを使うところを見せてくれたことでした。そこから私はAFLのソースコードを読み込み、内製のファジングツールチェーンの開発を始めました。
「また車輪の再発明か」と思う方もいるでしょう。はい、確かにそうです。ただ誇りを持って認めます。内製のファジングツールチェーンを持つことはゲームチェンジャーになると確信しているからです。ファジングの最大の欠点は、その周辺インフラのセットアップが非常に面倒だということです。それでも人々がファジングを使い続けるのは、それだけの価値があるからです。Zigにファジングが統合されれば、セットアップのコストが劇的に下がります。
現在何が実装されていて何が使えるのかをお見せするために、initプロジェクトを見てみましょう。zig build test を実行してみます。ソースコードを見ると「fuzzをzig testに渡してこのテストケースを失敗させられるか試してみてください」というコメントがあります。では実際にfuzzモードで試してみましょう。fuzzモードで実行すると、まずユニットテストを一度実行してどれがファズテストケースなのかを特定し、次にそのユニットテストをfuzzモードでリビルドします。fuzzモードではバイナリをインストルメント化し、ファジングがより効果的に機能するような処理も追加します。
このデモでは残念ながらビルドシステムにリグレッションが入っており、完全には動作していません。おそらく私自身が壊してしまったのだと思います。ただ何が起きているかは雰囲気で伝わると思います。fuzzモードはAutoDocsと同じWebAssemblyバイナリを使ったWebインターフェースを生成し、ソースコードを表示します。そしてカバレッジに応じて信号機のような赤と緑のインジケーターを各行に表示します。緑になった行はファザーが到達した行、赤はまだ到達していない行です。別のリリースモードで試すと少し動き始めます。ファザーはリリースモードで実行するのが望ましいです。より多くのイテレーションが回せるからです。しかし今回はどこかに問題があるようで、うまく動きませんでした。
10.2 気づきと仮説:設定コストの大幅低減によるゲームチェンジャーとしての可能性
Andrew: 現状は非常に実験的な段階です。しばらくこの作業から離れていたので、色々壊れているところもあります。ただこれがロードマップの一部であることは間違いありません。
ファジングをまだ試したことがない方はぜひ試してみてください。遺伝的アルゴリズムがコードをどのように探索するかは本当に驚異的です。どの入力が有効かを自ら学習し、コードを隅々まで探索していきます。バグをユーザーより先に見つけられる、それがファジングです。バグトラッカーなんていらない、全部見つけてしまえばいい、そういう発想です。
私が特に可能性を感じているのは、ファジングが単体テストを代替できるという点です。100個の単体テストを書いて何週間も費やすくらいなら、ファズテストを一つ書いて99個の単体テストを削除する、そういう世界が来ます。それほど強力なツールです。Zigにネイティブで統合されれば、設定のコストを大幅に下げることができます。今はセットアップが面倒だからこそ、使う人が限られています。しかし統合されれば、誰でも気軽に使えるようになる。小さなチームで多くのことを同時に進めているので時間がかかっていますが、これは確実に実現します。
11. コミュニティアナウンス(community mirrors / Software You Can Love Vancouver 2026 / Zig Days / Zigtoberfest)
11.1 コミュニティミラーの仕組みと運営ボランティアの募集
Andrew: ここからはコミュニティアナウンスに移ります。まず一つ目はコミュニティミラーについてです。CIをセットアップしている方はこの新しい仕組みに気づいているかもしれません。CIにZigを導入したい場合、ダウンタイムは困りますよね。そこでコミュニティミラーを活用することをお勧めします。
仕組みとしては、ボランティアがziglang.orgのtarballをミラーリングすることで、ZSFを支援できる仕組みです。Zigのユーザーとしてtarballを使いたい場合、コミュニティミラーを使うことでより高速なダウンロードとダウンタイムの回避が期待できます。というのも、ziglang.orgのアップタイムは保証していません。できる限り努力はしていますが、安価なサーバーで運用しており、誰もオンコールで待機しているわけではありません。夜中にサーバーが落ちても誰も飛び起きて対応するわけではないのです。ですからCIを安定させたい方はミラーをご利用ください。
すでにミラーを運営してくれている方々がいます。Emmy、Stevie、Linus Groh、Silver Squirrelの皆さん、本当にありがとうございます。またFrankも自分たちのTLS実装が完成次第ミラーを立ててくれる予定です。ミラーの詳細はウェブページに記載されていますので、ご興味のある方はぜひご覧ください。
11.2 Software You Can Love Vancouver 2026 の正式告知
Andrew: 二つ目のアナウンスは冒頭でもお伝えしましたが、改めて正式に告知します。Software You Can Love Vancouver 2026の開催が決定しました。ウェブサイトはまだ2023年のままですが、これは公式の告知です。Matt Knightが組織的な準備をすでに進めてくれており、Lorisも開催の手伝いをしてくれると聞いています。日程はまだ決まっていませんが、具体的なアクションはありません。ただ頭の片隅に入れておいてください。前回も非常に良いイベントになりましたし、今回はMatt Knightがさらにレベルアップしたイベントにするためのアイデアを持っているようです。楽しみにしていてください。
ちなみに面白いエピソードとして、ポートランドのすぐ北にもVancouverという地名があります。Software You Can Loveから帰宅するときにVancouverを出発すると、しばらく走ったところで「ようこそVancouverへ」という看板が出てきます。本当に不思議な体験です。
11.3 Zig Daysのフォーマット・Portland開催準備・オーガナイザー募集
Andrew: 三つ目のアナウンスはZig Daysです。zig.dayという新しいウェブサイトができました。妻が初めてのプルリクエストを送ってくれて、マップ上の小さなピンをZigのゼロのキャラクターに変えてくれました。本当にありがとう。
Lorisと私が気に入っているミートアップのフォーマットがあります。一日中講演を聞き続けるのではなく、朝9時に集まってアイスブレーカーを行います。30分ほどかけて参加者同士が顔を合わせ、その日に取り組もうと思っていることを全員が順番に話します。全員がノートパソコンを持参します。その後テーブルを囲んでグループを形成し、誰かの別のプロジェクトに参加したければそちらに合流することもできます。一日かけてそれぞれのハックセッションに取り組み、夕方5時頃に再び全員が集まって、その日に学んだことや取り組んだことを共有し合って解散します。個人的にはこのフォーマットが本当に楽しいと思っています。
Portland、つまり私が今住んでいる街でのZig Day開催を計画中です。まだ日程は決まっていませんが、友人のMasonが一緒に企画してくれることになっており、数ヶ月以内に実現したいと考えています。ミラノではつい最近5月17日に開催されたようです。サンフランシスコやVancouverはまだ準備中です。
このウェブサイトはイベントオーガナイザーへの参加呼びかけでもあります。自分の街でZig Dayを開催したい方は、Lorisに連絡するとその都市のサブディレクトリのコードオーナーとして登録してもらえます。自動化されたプルリクエストでウェブサイトを編集でき、イベントに来たい人向けのメール通知やRSS、iCalも利用できます。各イベントにはメール通知・RSS・iCalが用意されており、オーガナイザーになる方法はウェブサイト下部のリンクに記載されています。フォーマットは必ずしも私が説明した通りでなくても構いません。自分なりのアレンジを加えてください。もっと多くの場所でZig Daysが開催されることを楽しみにしています。
11.4 Zigtoberfest(ミュンヘン)の概要と前回の様子
Andrew: Q&Aに入る前に、もう一つ忘れていたアナウンスがありました。Zigtoberfestです。ウェブサイトを見ると最新情報が掲載されており、かっこいいロゴもあります。これはミュンヘンの応用科学大学で開催される一日カンファレンスです。午前中は主に講演が行われ、その後みんなで夕食に行くというような流れです。前回参加した人たちは楽しかったと言っていました。前回の録画もウェブサイトから見られます。ミュンヘン近郊にお住まいの方にはお勧めのイベントです。シュペッツレとプレッツェルが出るとのこと……それだけはちょっと気が進みませんが、スペッツェレさえなければ本当に良いイベントだったと聞いています。
12. Q&A
12.1 ブートストラップ・async IOのキャンセル実装・動的リンク境界を越えたasync IO
Andrew: では質問に答えていきましょう。まずx86_64 Linuxのデバッグビルドにおいてstage2コンパイラとstage3コンパイラは同じものかという質問です。このバイナリはこのファイルをコンパイルすることで生成されます。詳細はブートストラップについて書いたブログ記事を読んでいただくのが一番です。C++コードを排除した後のビルド方法について説明しています。簡単に言うと、このファイルはWebAssemblyから生成され、それを使ってコンパイラをビルドし、そこから最終的なコンパイラが生成されます。4回目以降は同じ結果が出続けます。
次に、async IOにおけるタイムアウトはユーザーが提供するキャンセルロジックで処理されるのかという質問です。これは良い質問です。IOを行うすべての関数のエラーセットにcancelledというエラーが追加されます。ファイルを読むような関数はさまざまな理由でエラーになり得ますが、そこにcancelledが加わるわけです。これをハンドルする必要がありますが、多くの場合はハンドルせずにエラーセットに含めたまま上位に伝播させるだけで良いでしょう。else returnの形で自然にバブルアップします。error deferがあればキャンセル時にもそれが走ります。実際にio_uringのプルーフオブコンセプトでは、キャンセルすると本当にカーネルへシスコールが送られて、io_uringのキュー内のインフライトな操作がキャンセルされます。非常にうまく機能しています。
動的リンク境界を越えたasync IOはどうなるかという質問についてはやや複雑です。externな関数にアノテーションを付けることで呼び出せるようにはなります。ただイベントループを持っているときに、サードパーティのコードがそのイベントループの外でファイルシステムの読み書きを勝手に行うと、目的が台無しになってしまいます。システムに参加してもらう必要があるのです。ただIO as an interfaceの設計上、一部のターゲットではlibCのコードも提供しており、そのlibCの関数がIOインターフェースを呼び出す実装を提供できます。その場合、呼び出す関数のスタック上限を計算でき、IOも制御できるため、最適な形で統合することができます。ただ動的リンクライブラリの向こう側のコードが悪さをすれば、たとえばsleep(100)を呼ぶだけでイベントループは台無しになります。うまく機能させるためのツールはたくさんありますが、保証はできないということです。
12.2 ビルドシステムの未解決課題・x86バックエンドとLLD排除方針・インクリメンタルキャッシュ問題
Andrew: ビルドシステムやパッケージ管理に大きな変更はあるかという質問です。長い間計画されてきた重要な未実装項目がたくさんあります。たとえばbuild.zigのロジックをWebAssemblyサンドボックスで実行するというのはかなり大きな変更です。破壊的でもあります。依存関係の更新を検出する機能もまだありません。依存関係の破壊的変更に対処して一部だけをパッチするための痛みのないワークフローもまだです。理論的には良い解決策があり、実装のアイデアもあるのですが、まだ実装されていません。やることは山積みですが、最も重要なものから順に取り組んでいきます。1.0を待っている方の気持ちはわかります。ただ私の個人的な目標は、1.0でなくても使う価値があると思ってもらえるくらいZigを compelling にし続けることです。不安定さを受け入れてでも使いたいと思ってもらえるほど良いものにする、それが目標です。1.0はまだ準備できていないのでタグを打ちません。
x86バックエンドとLLDの互換性についての質問もありました。正直なところ私はその問題に取り組む気はありません。代わりにLLD自体を完全に排除することに集中します。LLVMに立ち向かうのは何年もかかる超長期の取り組みですが、LLDの依存を排除するのはかなり近い将来に達成できます。ローカルな最大値に向けて努力するのではなく、大きな山を登ることだけに集中する、それが私のマントラです。LLDとの互換性を改善するための努力は一切せず、そもそも依存をなくしてしまえばその問題自体が消えます。
インクリメンタルコンパイルのキャッシュサイズ管理についても質問がありました。インクリメンタルはすでに存在する出力を最小限の変更で新しい状態に更新するよう設計されています。しかし現状では状態の堅牢なシリアライゼーションが未完成のため、デフォルトのキャッシュモードが毎回新しいアーティファクトを生成してしまっています。キャッシュのサイズをいじることで解決しようとは思っていません。すでにそちらは最適化済みです。取り組むべきはシリアライゼーションを完成させることで、それができれば既存のバイナリを上書き更新する本来の動作に戻れます。
12.3 MutexのIOインターフェースへの統合・ファイルシステム監視の標準ライブラリ化を見送る理由
Andrew: MutexやfutexはどのようにIO実装と統合されるのかという質問です。MutexはIO実装の一部です。インターフェース側ではmutexを整数として定義します。IO実装はその整数を使い、mutex_lockとmutex_unlockのvtable関数を実装する必要があります。重要なのは、ロックの処理はインターフェース側に実装されているという点です。状態はvtableの向こう側ではなく、インターフェース側に置かなければなりません。lockの実装は、まず仮想関数を呼ばずにロックを試みて、取得できなければ実装に委ねます。つまりインターフェースと実装が若干協調する必要があります。
スレッドプールベースのIO実装であればそのままmutexをロックするだけです。io_uringベースであればyieldして別の処理を開始し、mutexがアンロックされるまでそのfiberをスケジュールしません。IO実装に応じてmutexが適切な動作をするというわけです。微妙な調整が必要な部分ではありますが、うまく機能します。
ファイルシステム監視のコードを標準ライブラリから使えるようにする予定はあるかという質問もありました。ビルドシステムにはLinux・Windows・macOSそれぞれに異なるロジックを実装したファイル監視のコードがあります。これをより汎用的にしたいという気持ちはわかります。ただこれらのAPIを実際に使ってみた経験から言うと、ファイルシステム監視はあまり抽象化になじまないトピックだと感じています。アプリケーションのロジックと密結合する必要があることが多く、汎用化しようとすると無理が生じます。ですから標準ライブラリに汎用化して取り込む計画はありません。
12.4 アロケーターとIOの関係・std.io.Writer大規模リファクタリングの影響・ZSFへの財政支援方法
Andrew: アロケーターもIOインターフェースの一部にすべきではないかという面白い質問がありました。メモリへの書き込みもある意味でIOと見なせるし、メモリマップすればファイルへの書き込みとも言えます。これは興味深い観点です。グリーンスレッドのイベントループの実装を見てみると、実際にメモリを確保する必要があります。では一体どこからアロケーターを取得するのか。実は初期化時に渡してもらう形になっていて、スレッドセーフである必要があります。プラガブルではあります。一つの有効な答えとして、シングルスレッドブロッキングのIO実装を考えると、asyncを呼べばその場で実行して結果が返り、awaitはnoop、cancelも何もしない、mutexも何もしない、conditionも何もしない、すべて即座に実行される。そういったシンプルな実装においてはアロケーターの問題は消えてしまいます。メモリ確保がブロックする可能性の有無は実装によって変わるため、アロケーターとIOを完全に統合するかどうかはまだ考えの余地があります。
std.io.WriterとReaderの変更による大規模な破壊的変更についても触れておかなければなりません。これらを変更するとそれに触れるすべてのコードを書き直す必要があり、本当に大変な作業です。申し訳ないと思っています。ただこれが正しい方向だという確信があります。今回で四度目の挑戦であり、他のアプローチをすべて試した末の結論です。完璧でないものに妥協しないということが私の唯一の強みであり、その代償として破壊的変更が生じています。戦術を最近変えて、より少しずつ段階的に進めるようにしています。近いうちに進捗をお届けできると思います。
最後にZSF(Zig Software Foundation)への財政支援方法についてご案内します。ZSFは501c3の非営利法人です。501c6とは異なり、政府へのロビー活動は行いません。私たちはソフトウェアに集中する、政治的に中立な組織です。2024年の財務報告をウェブサイトで公開しています。2025年分も近いうちに公開予定です。支出と収入の内訳が確認できます。
支援方法としては主にEvery.orgとGitHub Sponsorsがあります。Every.orgも非営利組織であり、GitHubスポンサーよりも安定していると感じています。Microsoftの判断でGitHub Sponsorsが廃止されるリスクを考えると、Every.orgをメインの窓口として推奨しています。個人からの支援は私たちにとって特に嬉しいものです。収入を多様化して独立性を保つことができるからです。個人スポンサー・GitHub Sponsors・Benevityなど、さまざまな経路を通じて支援していただいています。また会社でZigを使っている方は、月1,000ドル程度の会社スポンサーシップをぜひ検討してください。資金が増えればより多くのZSFコントラクトを発注できます。ZSFのコントラクトを求めている人たちがたくさん待機しています。支援の方法がわからない場合はEvery.orgのZig Software Foundation Incのページをご覧ください。皆さんの支援に心から感謝します。Zigをより良くするために使い続けます。ありがとうございました。
