
AIを利用したコーディングが一般化し、新しいプログラミング言語の開発も加速しています。本日紹介する「Goose」は「ヒープアロケータなし、GCなし、ライフタイム注釈なし」でメモリ安全を実現する、新しいシステムプログラミング言語です。構文はCやRustに近く、C言語にコンパイルされます。
作者はWouter van Oortmerssen氏で、FlatBuffersやゲーム向け言語Lobsterの作者として知られています、言語設計と方針は人間が書き、約3万行のC++コンパイラ本体・ベンチマーク・サンプル・ドキュメントはほぼAI(Claude)が実装したとのこと。ライセンスは Apache 2.0 です。
Gooseのここが新しい
Gooseのいちばんの特徴はメモリモデルです。可変長の値も含め、すべてのデータはコンパイラが管理する「データスタック」上にインラインで置かれます。伸びるときはバンプポインタを進めるだけ、解放はスコープを抜けたときに行われます。mallocもfreeも出てきません。
以下のような特徴を持っています:
-
参照がダングリングしない: 配列が伸びても要素は動かないので、
pushが返した参照は100万回pushした後でも有効です。ライフタイムは静的に推論され、注釈もunsafeも不要です。 - 可変長データもインライン: 構造体は可変長フィールドをポインタなしで持てます。公式の例では注文データ1件が29バイト、アロケーション0回です。
- 最終位置での構築: 関数の戻り値は呼び出し元の格納先に直接作られ、コピーが発生しません。
-
フレームをまたぐエラー返却:
return err from loadで、途中の関数を飛ばして外側のload呼び出しから直接戻れます。例外もResult型も?も使わず、コンパイラが静的に検査します。 -
相対参照とシリアライズ:
T&<u32>はオフセットで参照するのでデータが位置に依存しません。to_bytes()でそのままバイト列にでき、from_bytesは検証してから読み込みます。 - 共有メモリのないスレッド: ワーカーは独立したプログラムとして動き、型付きキューでフラットなデータをやり取りします。
- ADTとcase関数: enumのバリアントごとに関数をオーバーロードでき、網羅性もチェックされます。継承やvtableはありません。
配列は用途ごとに種類が分かれていて、型を見れば伸び方がわかります。
|
型 |
意味 |
|---|---|
|
|
固定長 |
|
|
容量上限つき |
|
|
伸びるだけ(grow-only) |
|
|
伸び縮みする |
|
|
スライス(コピーなしのビュー) |
速度面でも、公式の16本のベンチマークでC++やRustより速く、メモリ使用量も少ないとされています。
コードはどんな感じ?
以下は公式チュートリアルからの引用です。まずは Hello World。
fn main() {
print("hello, goose");
}
伸びる配列と参照。.=は値のコピーではなく参照を束縛する演算子です。100万回pushした後もfirstは安全に使えます。
struct Item { id: i32, weight: f32 }
var items: Item[>..] = [];
let first .= items.push(Item { 1, 0.5 }); // a reference to element 0
for i in 2..1000001 { items.push(Item { i as i32, 0.0 }); }
first.weight = 99.5; // still valid, a million pushes later
print(items.len, " items, first is ", items[0]);
enumとcase関数。バリアントごとのオーバーロードが、タグで振り分けられます。
enum Shape { Circle { r: f64 }, Rect { w: f64, h: f64 }, Dot }
fn area(s: Shape.Circle) -> f64 { 3.14159 * s.r * s.r }
fn area(s: Shape.Rect) -> f64 { s.w * s.h }
fn area(s: Shape.Dot) -> f64 { 0.0 }
ほかにも、多値返却(let q、ok = divide(7、2);)、ifやmatchが式であること、参照の同一性を比べる.==など、小さな工夫が積み重ねられています。
C・Rustとの違い、そして割り切り
Goose は「Rust の安全性を、借用チェッカー(borrow checker)と格闘せずに実現する」を狙った言語です。その代わり、表現力の一部を意図的に手放しています。
|
観点 |
C |
Rust |
Goose |
|---|---|---|---|
|
メモリ管理 |
手動 malloc/free |
所有権+ヒープ |
データスタック、ヒープなし |
|
メモリ安全 |
なし |
あり |
あり |
|
ライフタイム注釈 |
不要 |
必要な場合あり |
不要(静的推論) |
|
エラー処理 |
戻り値 |
|
|
|
クロージャ・関数ポインタ |
関数ポインタのみ |
あり |
なし |
|
分割コンパイル |
あり |
あり |
なし(プログラム全体) |
公式が挙げる制約は次のとおりです。
- データの所有とスコープは自分で設計する必要がある
- 再帰関数では、伸びるデータを再帰呼び出しをまたいで保持できない
- 可変長要素の配列はイテレートはできるがインデックスアクセスはできない
- 動的ディスパッチは ADT のタグによるものだけ
向いていそうなのは、ゲームやシミュレーションなど、データレイアウトと速度を細かく制御したい場合です。標準ライブラリにはgfx(SDL3 GPU)、physics(Box3D)、ui(Nuklear)、audioなど14モジュールがあり、その方向性がよく表れています。Cの関数もextern fnで直接呼べます。
試してみるには?
対応環境は Windows(x64)、Linux(x64)、macOS(Apple Silicon / Intel)です。コンパイラに TinyCC が組み込まれているので、ビルド不要でその場で実行できます。
git clone https://github.com/aardappel/goose cd goose cmake -B build -DCMAKE_BUILD_TYPE=Release -DGOOSE_BUILTIN_LIBS=all cmake --build build # その場で実行 build/goose samples/01_tour.goose # 実行ファイルを作る build/goose build samples/01_tour.goose && samples/01_tour

スレッドを使うプログラムは、システムのCコンパイラでビルドする必要があります。
まとめ
Goose は「ヒープを捨てる」という大胆な割り切りで、安全性・速度・書きやすさを両立しようとする言語です。まだ生まれたばかりで、可変長値のムーブやラベル付き break などは今後の課題とされています。
AIが実装の大半を担った言語としても興味深い事例で、Rustに疲れた人も、一度触ってみると新しい発見があるかもしれません。
なお、Hacker Newsでも大きな話題になりましたが、評価は今のところ「アイデアは面白い、ただし主張とドキュメントには疑問も」という賛否両論となっています。
