介紹 Spectre:一種基於合約的底層系統程式設計方法

系統程式設計的領域長期以來一直在效能與安全性之間進行拉鋸。雖然像 C 和 C++ 這樣的語言提供了對硬體的無與倫比的控制力,但它們也為記憶體損壞和未定義行為留下了大門。Rust 透過所有權和借用機制在解決這些問題方面取得了重大進展,但對於那些優先考慮透過合約實現形式化正確性的語言,仍然存在一個利基市場。

Spectre 正式進入這個領域,這是一種專為安全、基於合約的底層系統程式設計而設計的程式語言。透過整合型別層級的不變量(invariants)以及明確的前置條件(preconditions)與後置條件(postconditions),Spectre 旨在讓底層開發變得更加可預測且在數學上是健全的,同時又不犧牲開發者體驗。

核心哲學:設計即正確

Spectre 的核心建立在一個前提之上:正確性應該在語言層級被強制執行,而不是完全交由開發者的紀律或外部測試套件來處理。該語言專注於三個主要支柱:

  1. 預設不可變性:為了確保合理的數據流並減少與副作用相關的錯誤,Spectre 將數據視為不可變,除非另有明確說明。
  2. 基於合約的程式設計:Spectre 允許開發者在型別層級定義不變量,並在函數層級定義前置條件/後置條件。這確保了函數在被呼叫時具有有效的狀態,並能回傳預期的結果。
  3. 混合驗證:為了避免與 Z3 等 SMT solver 相關聯的極端複雜性和潛在的「無法證明」障礙,Spectre 採用了一種務實的驗證方法。合約會在盡可能的情況下於編譯時進行評估。如果編譯器無法證明某個條件,該檢查將自動延遲到執行時。這些在正式版本中持續存在的執行時檢查,可以使用 guarded 建構式來管理。

記憶體管理與後端架構

與高階受管語言不同,Spectre 透過使用手動記憶體管理來保留底層控制權。開發者通常透過標準函式庫分配器(例如 Arena 或 Stack 分配器)或透過實作自定義分配器來與記憶體互動。這確保了該語言仍適用於核心開發、嵌入式系統和其他高效能應用程式。

從編譯的角度來看,Spectre 旨在具備靈活性。主要的流水線將高階程式碼編譯成 QBE IR,然後將其降低(lowered)到特定平台的組合語言。為了擴大其影響力與相容性,該語言也提供了 LLVM 和 C99 的實驗性後端。

對於採用而言,最實用的功能之一是 --translate-c 旗標。這允許現有的 C 程式碼被轉換為等效的 Spectre 程式碼,顯著降低了將舊有專案遷移到更安全、以合約為導向的環境的門檻。

使用 trust 關鍵字處理不純性

Spectre 引入了一種處理不純操作(impure operations)的獨特方法。在提供的 "Hello World" 範例中,對於依賴底層不安全機制的操作,使用 trust 關鍵字是強制性的:

val std = use("std")

pub fn main() i32 = {
    trust std.stdio.print("Hello, world: {d}.", {10})
    return 0
}

任何本質上是不純的操作——例如某些 I/O 操作——都必須被明確地信任。這迫使程式設計師承認系統中「不安全」的部分所在何處。然而,該語言區分了不同的風險等級;例如,標準函式庫中的一個簡單 @puts 呼叫被標記為安全,只要沒有發生像記憶體不足(OOM)錯誤這樣的災難性故障,就不需要 trust 關鍵字。

社群觀點與評論

雖然 Spectre 的技術目標非常宏大,但社群對其定位與實用性提出了一些關鍵問題。

"Rust" 的比較

一些觀察者質疑 Spectre 在當前生態系統中的位置,並指出關注函數不變量的概念並不新穎。一位評論家指出,若沒有更廣泛的功能集,該語言可能會被視為「沒有所有功能的 Rust」。

人機工程學 vs. 安全性

trust 關鍵字也是爭議點之一。雖然它作為一個安全標記,但一些開發者認為它增加了程式碼的冗餘性。正如一位評論者所說:

"just document the impure operations and stop forcing the programmer to type extra characters."

絕對安全性的問題

最後,關於 Spectre 是否「真正安全」(意即在安全程式碼中完全不存在未定義行為)或它僅僅是提供了一套安全功能,但仍為關鍵錯誤留下了漏洞,這是一個持續進行的辯論。作為一種手動記憶體管理語言,Spectre 中「安全」的定義是在開發者控制權與編譯器強制保證之間的細微平衡。

Sources