原文:The Goose Programming Language,作者 Wouter van Oortmerssen(Lobster、Amiga E 作者)。
效能數字為專案自報 benchmark,未經第三方驗證。以下為正體中文翻譯整理,程式碼保持原文。
一句話說完
Goose 是 memory-safe 的系統語言,沒有 heap allocator、GC、lifetime 標註。所有動態值住在編譯器管理的 data stack 上,增長只推 pointer,scope 結束即釋放。專案自稱 16 項 benchmark 跑贏慣用寫法的 C++ 3.3 倍、手動優化 C++ 1.16 倍、最佳 safe Rust 1.12 倍,記憶體更少。
為什麼用 Goose
- 比 C++ 和 safe Rust 快,同時 memory safe:增益來自資料排列與記憶體模型。
- 沒有 allocator、GC、reference counting、destructor:記憶體是編譯器靜態配置的幾個 data stack,釋放百萬元素結構只需一次 store。
- 增長不讓 reference 失效:指向 growing array 內部的 reference 一直有效。C++ 得靠
reserve,safe Rust 得用u32索引,Goose 直接保留 typed reference。 - 零標註的 memory safe:沒有 lifetime 語法、沒有 aliasing 規則、沒有
unsafe,編譯器推導每個 reference 的 root。 - 巢狀資料保持 inline:字串、字串陣列、變長欄位 record 都是連續區塊。C++ 要 160 bytes 加一次配置的 record,Goose 只要 29 bytes、零配置。
- Enum 只花該 variant 的成本:variable-mode ADT 讓每個值只佔自己 variant 的大小。
- 比指標更窄的連結:relative reference 存成 1、2、4-byte offset,結構 position independent,資料結構本身就是檔案格式。
- 保證在原地建構:值直接在最終目的地建好,
items.push(parse(line))把結果直寫進陣列。 - 跨多層呼叫回傳:
return err from load可從任意上層 frame 回傳,靜態檢查,不需 unwinder。 - 互不共享的執行緒:worker 各有記憶體,flat 值經 typed queue 以
memcpy傳遞,沒有 data race、lock、atomic。 - 零開銷泛型與高階函式:untyped 參數即泛型,
xs.filter() { it > 0 }編出來就是迴圈。 - Plain C in,plain C out:編成單一 C 檔,經
extern fn直接呼叫 C,內附 TinyCC 可即時執行。
記憶體模型:stack,離開 scope 即 free
程式有 native call stack、靜態資料和 N 個 data stack,N 由編譯器決定。data stack 是帶 bump pointer 的大段位址空間保留,沒有其他記憶體。每個 stack 同時最多一個可變大小的值存活且永遠在頂端,所以增長永不搬移、永不檢查容量。
for round in 3 {
var scratch: u8[>..] = [];
for i in 100000 { scratch.push((i % 256) as! u8); }
print("round ", round, ": ", scratch.len, " bytes");
}
什麼都不搬,reference 在增長後仍有效
struct Item { id: i32, weight: f32 }
var items: Item[>..] = [];
let first .= items.push(Item { 1, 0.5 });
for i: i32 in 2..1000001 { items.push(Item { i, 0.0 }); }
first.weight = 99.5;
push 回傳指向新元素的 reference。vector 或 Vec 可能重配置,給不出這種保證。
陣列一族
| 寫法 | 意義 |
|---|---|
T[k] |
固定大小,編譯期已知 |
T[]、T[varint] |
建構時定大小,之後凍結 |
T[..k]、T[..] |
容量 inline,在容量內增減 |
T[>..] |
grow-only 主力,內部 reference 保持有效 |
T[>..<] |
grow-shrink:stack、queue、heap 用 |
字串就是 u8 陣列:u8[>..] 是 builder,u8[] 是完成品,u8[..16] 是 struct 內的小字串,u8[:] 是 view。
代價
- 得想資料住哪:擁有者與 scope 長度對不上就沒效。
- 遞迴函式不可在遞迴呼叫兩側保留可增長資料。
- 變長元素陣列可走訪、不可索引。
- 沒有逃逸閉包、沒有 function pointer、只做 whole-program 編譯。
- 歸還給 pool 卻仍被具名的 slot,讀到下一個主人的資料:邏輯 bug,不是記憶體損毀。
現狀與授權
約三萬行 C++ 的 whole-program 編譯器、規格書、標準函式庫、31 支範例,CI 跑 Windows、macOS、Linux。Apache 2.0 授權。刻意暫不做:resizable 值的 move、單 struct 多 resizable、labeled break、namespace 隱私。
作者與 AI
專案幾乎全由 AI 產出,源自人類的語言設計。作者多年前設計好 Goose 並動手實作,因經營遊戲新創(基於他的另一個語言 Lobster)沒時間收尾。Fable 出現後,他讓 AI 照 Lobster 編譯器的風格結構接手,來回討論定版。編譯器用 C++ 寫:輸出本來就是 C,同生態系簡化部署,也方便整合 libtcc 等 C 函式庫。
HN 網友反應(320 分,214 則留言)
| 主題 | 風向 | 重點 |
|---|---|---|
| 記憶體模型設計 | 叫好 | 讚 stable reference、relative link、位元組即檔案格式 |
| 文件像 AI 寫 | 開罵 | 要求作者自己重寫,只講做法不講動機 |
| Benchmark 對照組 | 存疑 | Rust 對照組用 Vec::new() 而非 with_capacity() |
| Carmack 是否背書 | 爭議 | 被指標題誤導,作者稱對方有讀 repo |
| 缺點只放教學 §18 | 觀感不佳 | 像把缺點藏起來 |
作者全程 AMA:執行緒學 Dart Isolate,各自獨立、以 memcpy 傳 flat 值;AoS 轉 SoA 目前不做;Lobster 走好用路線,Goose 走極速路線。
懶人包
互動版整理在一頁內:
手機版可直接開:Goose 語言懶人包
