後端工坊 2026 年 4 月 23 日

2026-04-23 — Safe Rust 的 GC 實作、2026 ISO C++ 問卷開放、新嵌入式腳本語言 Nondescript 發布

Take the 2026 ISO C++ Develope…

Take the 2026 ISO C++ Developer Survey!

devblogs.microsoft.com · 2026-04-22

每年一度的 ISO C++ 開發者問卷又回來了。由 Standard C++(isocpp.org)主辦、Microsoft C++ 團隊積極推廣的「2026 Annual C++ Developer Survey "Lite"」現已開放填寫,預計作答時間約 10 分鐘。

為什麼這份問卷重要?

C++ 的標準化流程相對保守,ISO WG21 委員會必須在向後相容性、效能、安全性與語言複雜度之間不斷取捨。這份年度問卷是社群最直接能影響委員會決策方向的管道之一。根據 Microsoft C++ 團隊資深產品經理 Augustin Popa 的說明,問卷結果會直接送交標準化委員會與工具鏈廠商參考,讓整個 C++ 生態系的演進能夠朝著開發者實際需要的方向走。

換句話說,如果你在意 C++26 或 C++29 要往哪個方向發展——無論是更好的反射(Reflection)支援、靜態分析基礎設施、模組系統改善、協程 API、還是 Safety profile——填這份問卷就是你的機會。

歷年問卷對標準的影響

過去幾年的調查結果曾顯示,開發者對以下議題最為關心:

  • 建置速度與 Modules 的採用障礙
  • sanitizer 與靜態分析工具整合的需求
  • 記憶體安全相關特性(尤其是在 Rust 崛起的背景下)
  • C++ 工具鏈(編譯器、套件管理器)的互通性

WG21 的提案作者往往會引用這類問卷數據來支持自己的提案,因此社群聲音確實有機會形塑未來標準的優先順序。

對後端工程師的意義

對於每天用 C++ 寫高效能後端服務、嵌入式系統或底層基礎設施的工程師來說,這份問卷提供了一個難得的機會:讓標準委員會知道你在工作中真正遇到的痛點,而不是讓語言特性由學術驅動或少數大廠主導。

特別值得關注的是,近幾年「C++ 安全性」議題的討論熱度大幅提升(NSA 與 CISA 等政府機構已多次建議轉移到記憶體安全語言),WG21 也在積極研擬 Profiles 機制以改善現有程式碼的安全性。這屆問卷的回答內容,很可能左右委員會在 Safety 相關提案上投入多少政治資本。

如何參與

前往 Standard C++ 官網(isocpp.org)即可找到問卷連結。問卷以英文為主,主要詢問你的使用情境、最常用的編譯器與工具、對各類語言特性的重視程度,以及你認為未來最應優先解決的問題。填完之後也歡迎轉發給團隊中其他 C++ 工程師,因為回答數量越多,數據越具代表性。


The Edge of Safe Rust

kyju.org · 2026-04-22

這篇文章源自 TokioConf 2026 的一場演講,作者深入探討了 Rust 在處理「循環指標」(cyclic pointers)時面臨的根本困難,以及 Rust 生態系中為此衍生出的一系列奇特而有趣的解法。文章的核心論點是:Rust 的所有權模型(ownership)在應付循環參考時天生就很吃力,而要在 safe Rust 內做到追蹤式垃圾回收(tracing GC),需要走過一段相當曲折的路。

問題根源:所有權模型與循環參考

Rust 的記憶體管理依賴所有權與借用規則,編譯器能在 compile time 追蹤物件生命週期,讓大多數記憶體錯誤在執行前就被攔下。但這套系統的阿基里斯腱正是循環參考。當 A 持有 B 的參考、B 又持有 A 的參考時,沒有任何一方的 Drop 可以被安全地先執行。Python、JavaScript 這類語言用的是基於可達性(reachability)的 GC,天生就能處理這種情況;Rust 的 Rc<T> 搭配 Weak<T> 只能算半套解法,需要開發者手動決定哪條邊要弱化。

方法一:用 Vec 索引取代指標

第一種做法是拋棄指標,改用 Vec 加上「世代索引」(generational index)。每個節點存在 Vec 的某個槽位中,指向其他節點的「指標」其實是一個 (index, generation) 組合。generation 計數器解決了 ABA 問題——當一個槽位被釋放後重新分配給新物件時,舊的索引因為 generation 不符而失效,避免了 use-after-free。這個方法的代價是將 compile-time 保證下沉到 runtime:每次存取都要做邊界檢查與 generation 比對。好處是實作簡單,不需要 unsafe,整個資料結構可以輕鬆序列化。

方法二:用 bumpalo 做有限制的循環參考

第二種方法利用 bumpalo arena allocator,讓所有節點共享同一個 lifetime 'a,然後用 Cell<Option<&'a Node<'a>>> 儲存可變的循環參考。因為所有節點的 lifetime 相同,這種寫法可以通過借用檢查器。然而這個方法有嚴重限制:它禁止 Drop 的實作(否則生命週期語義會出問題),而且產生出來的型別是 !Send,無法跨執行緒傳遞。這讓它在實際的伺服器端應用中用途受限。

方法三:帶「品牌」的指標與追蹤 GC(gc-arena)

文章最核心的部分介紹了 gc-arena crate 的設計,這是一個真正在 safe Rust 邊界內實現追蹤式 GC 的方案。它的關鍵創新是「泛型化」(generativity)技術。

概念是這樣的:利用 Rust 的高階特徵邊界(HRTB,for<'a> 語法),每次進入 arena 時都產生一個在 type 層面獨一無二的 lifetime。搭配 PhantomData<Cell<&'id ()>>,這個 lifetime 被「烙印」(brand)在 GC 指標型別上:

// Gc<'gc, T> 是一個不透明的 GC 指標
// 'gc 是 arena 的品牌 lifetime,確保 Gc 不能逃出 arena
struct Gc<'gc, T> {
    ptr: NonNull<GcBox<T>>,
    _phantom: PhantomData<(&'gc (), T)>,
}

// Gc 實作了 Copy 和 Deref,讓使用體驗接近原生指標
impl<'gc, T> Deref for Gc<'gc, T> {
    type Target = T;
    fn deref(&self) -> &T { unsafe { &(*self.ptr.as_ptr()).value } }
}

因為 Gc<'gc, T> 實作了 Deref,它的 Send/Sync 特性就由 T: Sync 決定,與一般參考的行為一致。最關鍵的是,Gc 只能在 arena 的 enter 閉包內被使用——閉包結束後,所有 GC 指標的 lifetime 就失效了,Rust 編譯器會拒絕讓它們逃逸。這樣一來,GC 的追蹤(marking)就可以在閉包結束後安全地進行。

Trace trait 與多型別 GC

追蹤式 GC 需要知道哪些物件是可達的。gc-arena 透過 Trace trait 讓使用者告訴 GC 如何遍歷自己的資料結構:

unsafe trait Trace<'gc> {
    fn trace(&self, cc: &Collection);
}

搭配 Generic Associated Types(GAT),gc-arena 還能支援多種型別共存於同一個 arena 中,讓它接近一個完整的動態語言執行環境所需要的 GC。

已有大型專案在用

這不只是學術練習。作者列舉了兩個真實在用 gc-arena 的大型開源專案:

  • Ruffle:開源 Flash 模擬器,用 gc-arena 為 ActionScript VM 提供 GC 支援。整個 Ruffle 歷史中因為這套 GC 引發的 bug 僅有三個。
  • Fields of Mistria:一款正式商業遊戲,其 GameMaker 替代執行環境 fabricator 同樣基於 gc-arena。

更深的哲學觀點

文章最後提出了一個值得系統程式語言設計者深思的論點:「系統程式語言,應該是可以同時擁有任意數量的垃圾回收器的語言。」這與 Erlang 的設計理念呼應——每個 process 有自己獨立的 GC,而不是共用一個全局的 stop-the-world collector。多個隔離的 GC 對 tail latency 的影響遠小於單一的全局 GC,且每個 GC 可以根據其工作負載做特化。gc-arena 的設計讓 GC 成為「只是一個高階資料結構」,可以自由地在 Rust 程式中組合、嵌套。

限制與適用範圍

這個設計的主要限制是:GC collection 不能發生在 Arena::enter 閉包的執行期間——閉包必須先退出,才能進行 marking 和 sweeping。這表示它不適合需要在無限迴圈中持續執行、同時要求 GC 隨時介入的場景。不過作者認為,實務上這個限制比想像中少見,多數場景都可以設計成「進入 arena → 執行一段邏輯 → 離開 arena → 觸發 GC」的循環。

工程師可以嘗試的方向

如果你正在 Rust 中實作直譯器、DSL 執行環境、或任何需要循環資料結構的系統,gc-arena 是一個值得認真評估的選項。它的 API 設計相當嚴謹,透過型別系統保證了安全性,且生產環境的穩定性已由 Ruffle 和 Fields of Mistria 驗證。從這篇文章也可以學到:Rust 的型別系統其實比表面上更有表達力,generativity 和 HRTB 這類進階技巧能解決很多看似需要 unsafe 的問題。


nondescript: a simple embedded programming language

github.com · 2026-04-22

Nondescript 是一個新發布的嵌入式腳本語言,目標是佔據與 Lua 相似的生態位:可以輕鬆嵌入任何 C 應用程式、單一檔案實作、無外部依賴。整個專案於 2026-04-22 完成初始提交,以 LGPL-2.1 授權釋出。

設計定位:Lua 的替代選項

Lua 長期以來是嵌入式腳本語言的事實標準——遊戲引擎、資料庫(Redis 的 Lua 支援、ClickHouse 的 UDF)、網路設備的配置系統都廣泛使用它。但 Lua 有一些讓人不太滿意的地方:1-based indexing 讓從其他語言過來的開發者困惑、語法與大多數現代語言差異顯著、API 設計雖然精巧但學習曲線不低。Nondescript 選擇了一條截然不同的路:AppleScript 風格的自然語言語法。這是一個相當罕見的設計選擇,在 2026 年的語言設計圈裡算是一種復古的異類。

技術規格

  • 語言標準:C11(嚴格來說是 C99 加上 _Alignof
  • 實作大小:單一 .c.h 檔,無外部依賴
  • 授權:LGPL-2.1
  • 整合指令cc -o myapp myapp.c nondescript.c -lm
  • 實作語言:100% C

語言特性

Nondescript 的語法靈感來自 AppleScript,採用大小寫不敏感(case-insensitive)、接近自然語言的表達方式。主要語言特性包括:

  • Chunk accessors:可以用 word 1 of myString 這樣的語法存取字串的第幾個詞,類似 AppleScript 的 chunk 語法
  • List comprehensions:支援列表推導式,方便集合操作
  • Given blocks:允許將程式碼區塊作為參數傳入函數,這是一種輕量級的高階函數機制
  • Extensible grammar:宿主應用程式可以注入自訂關鍵字與語法規則,讓腳本語言與領域語言(DSL)無縫結合
  • Pluggable allocators:支援自訂記憶體配置器,包括池式分配(pool allocator)和記憶體使用量上限設定
  • Slot-based C API:透過 slot(槽位)進行值的傳遞,C 層呼叫腳本函數或腳本呼叫 C 函數都使用同一套 API

Slot-based API 設計

Slot-based API 是這類嵌入式語言的標準設計——Lua 用的也是類似的 stack-based 方式,差別在於 Nondescript 明確使用「slot」的命名。這種設計讓 C 與腳本之間的 FFI 不需要複雜的型別映射層,值的傳遞直接透過整數索引操作:

// 概念示意(具體 API 詳見 nondescript.h)
nd_push_string(vm, 0, "hello");    // 把字串放到 slot 0
nd_call(vm, "greet", 1, 1);        // 呼叫函數,1 個參數,1 個回傳值
const char *result = nd_get_string(vm, 0); // 從 slot 0 取回傳值

自然語言風格的語法範例

README 中附有 calculator 和 turtle graphics 兩個示範程式,展示了 Nondescript 的語法風格:

-- 定義函數(natural-language style)
to add x to y
  return x + y
end

-- Chunk accessor 範例
set greeting to "hello world"
set firstWord to word 1 of greeting  -- "hello"

-- Given block(把程式碼當參數傳入)
repeat 4 times given [
  move forward 100 steps
  turn left 90 degrees
]

這種語法風格讓腳本在沒有程式背景的使用者眼中看起來更像「指令」而非「程式碼」,在自動化測試、RPA、遊戲 mod 支援等場景中是很有價值的特性。

與 Lua 的比較

Nondescript 與 Lua 的主要差異:

  • 語法哲學:Lua 選擇簡潔的類 Pascal 語法;Nondescript 選擇自然語言風格
  • 大小寫敏感:Lua 區分大小寫;Nondescript 不區分
  • 成熟度:Lua 有 30 年歷史,龐大生態系;Nondescript 剛剛誕生(7 個 commits)
  • 效能:Lua(尤其是 LuaJIT)有非常成熟的效能優化;Nondescript 目前無 JIT
  • 特色功能:Nondescript 的 chunk accessor 和 extensible grammar 是 Lua 所沒有的

目前狀態與已知問題

這個專案目前仍屬於早期階段。README 的 TODO 區塊坦承了幾個尚未完成的部分:byte/character 處理不完整、文件有缺口、程式碼需要清理。整個 repo 到目前為止只有 7 個 commit,都在同一天(2026-04-22)完成,是一個剛剛公開的早期版本。目前 GitHub 上有 2 顆星,貢獻者只有作者 Rob King(deadpixi)一人。

工程師可以嘗試的方向

如果你正在為某個 C/C++ 應用程式尋找嵌入式腳本語言,Nondescript 是個有趣的新選項——特別是當你的使用者群需要一個「更人性化」的語法時。當然,Lua 在生產環境的驗證程度目前遠超過 Nondescript,不建議在高風險系統中立即採用。但作為週末探索的技術實驗、或研究「如何設計嵌入式腳本語言 API」的參考案例,它的程式碼(單一 .c 文件、C11 標準)非常適合閱讀學習。Extensible grammar 的設計思路尤其值得深入研究——這是大多數嵌入式語言不提供的特性,讓宿主應用程式可以把腳本語言真正打造成領域專屬語言(DSL)。


來源:devblogs.microsoft.com, kyju.org, github.com

End of article
0
Would love your thoughts, please comment.x
()
x