AIが実装した新言語「Goose」、mallocなしでRustより速い?

[PR]記事内のアフィリエイトリンクから収入を得る場合があります
  • URLをコピーしました!

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はありません。

配列は用途ごとに種類が分かれていて、型を見れば伸び方がわかります。

型

意味

T[k]

固定長

T[..k]

容量上限つき

T[>..]

伸びるだけ(grow-only)

T[>..<]

伸び縮みする

T[:]

スライス(コピーなしのビュー)

速度面でも、公式の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

所有権+ヒープ

データスタック、ヒープなし

メモリ安全

なし

あり

あり

ライフタイム注釈

不要

必要な場合あり

不要(静的推論)

エラー処理

戻り値

Result と ?

return ... from

クロージャ・関数ポインタ

関数ポインタのみ

あり

なし

分割コンパイル

あり

あり

なし(プログラム全体)

公式が挙げる制約は次のとおりです。

  • データの所有とスコープは自分で設計する必要がある
  • 再帰関数では、伸びるデータを再帰呼び出しをまたいで保持できない
  • 可変長要素の配列はイテレートはできるがインデックスアクセスはできない
  • 動的ディスパッチは 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でも大きな話題になりましたが、評価は今のところ「アイデアは面白い、ただし主張とドキュメントには疑問も」という賛否両論となっています。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次