FIDL 設計原則

本頁歸納了 FIDL 隨著時間的推移採用的主要設計原則。

民眾的優先順序

FIDL 旨在尊重以下消費者的優先順序:

  1. 使用者 (使用 Fuchsia 產品)
  2. 開發人員 (使用 FIDL 繫結)
  3. Fuchsia 協作者 (使用 FIDL 繫結)
  4. API 設計人員 (編寫 FIDL 程式庫)
  5. Fuchsia FIDL 團隊成員

這份清單是根據 API 委員會憲章規定改編而成。

ABI 優先,API 秒鐘

來源為 RFC-0050:語法 revamp

FIDL 主要在定義應用程式二進位檔介面 (ABI) 疑慮,以及應用程式設計介面 (API) 相關問題。

二進位線格式優先

來源為 RFC-0050:語法 revamp

雖然有許多格式可以代表 FIDL 訊息,但 FIDL 傳輸格式 (或稱「FIDL 二進位線路格式」) 是優先處理的方式,且為最先...我們選擇在選擇語法時,選擇過度旋轉為二進位 ABI 格式。

從低到低

來自 RFC-0131:FIDL 線路格式的設計原則

在設計上為了支援低階程式設計而犧牲高階程式設計能力時,我們通常選擇啟用低階程式設計。

最少功能

來源為 RFC-0050:語法 revamp

我們致力於提供最少的功能和規則,並致力結合多項功能來實現用途。實際上,在考慮新功能時,我們應該先嘗試調整或一般化其他現有功能,而非推出新功能。

用多少付多少

RFC-0027 起:您用多少就付多少

為 FIDL 新增功能時,應評估新增該功能會對使用 FIDL 但未使用新功能的使用者產生的影響。針對不使用這項功能的使用者,我們應設有非常高的門檻,用來接受這類功能的使用者會產生費用。

舉例來說,RFC-0047: Tables 遵循這項原則,其次是為語言新增資料表,而不是替換結構:

資料表比結構複雜,因此處理速度會比較慢,序列化資料表會耗用較多空間。因此,最好將結構保持原樣,並引入新的內容。

相反地,RFC-0061:可延伸聯集就達到了相反的決定,即以可延伸聯集取代靜態聯集,但必須先仔細分析權衡的取捨。與資料表不同,擴充聯集產生的額外費用在大多數情況下為邊緣或不存在。

解決真實問題

我們設計 FIDL 來解決實際問題並解決實際需求,而非想像中。我們避免設計「找出問題的解決方案」。

舉例來說,FIDL 一開始因為不清楚如何在 C/C++ 中表示空白結構體,因此一開始並不支援。在 RFC-0056: Empty structs 中,我們發現使用者使用了變通方法,並且察覺到正式解決方案的需求。我們只接著在語言中新增空白的結構體。

根據資料進行最佳化

在沒有資料的情況下進行最佳化,在最壞的情況下是最佳的最佳化風險,也最危險。在設計最佳化項目 (例如效能、二進位檔大小) 時,我們會追蹤資料。

舉例來說,我們一開始接受 RFC-0032: Efficient 依,但後來遭到拒絕。但因為沒有可備份的資料,因此目前不應接受。之後,當系統顯示可大幅提升效能的相關資料後,又再次提出解決方案,並成為 RFC-0113: 有效率的信封

同樣地,使用資料表資料的稀疏表示法後也獲得了顯著的成長。然而,經過調查後,我們發現設計複雜度與效能之間存在著不利的取捨,而這導致機構決定不前進 (請參閱 RFC-0116:Sparser Tables)。如果沒有重要的原型設計和資料收集階段,稀疏資料表可能已經採用,並對 Fuchsia 造成負面影響。

遠處沒有破損

我們力求避免在遠處就遭破壞。在同一處進行的變更不應造成遠處的破壞。例如,如果將名為 Foo 的結構新增至 FIDL 檔案會破壞編譯,就會令人意外,因為程式碼集完全不同部分的 FIDL 檔案已有名為 Foo 的類型。因此,如同大多數程式設計語言,FIDL 都使用命名空間來限制名稱衝突的範圍。

RFC-0029:增加方法序數會討論這個問題,因為這與通訊協定組合有關。RFC-0048:明確聯集序數會回顧主題,說明 FIDL 只針對通訊協定使用雜湊的原因。

RFC-0057:預設的「沒有控制代碼」會導入值與資源類型之間的區別。這樣做的一個動機是為 Rust 提供 Clone 特徵,適用於無遠距離且沒有控點的類型:

雖然 FIDL 繫結可以根據帳號代碼有條件地啟用程式碼,但這樣做不是理想做法,因為它會破壞效能性保證。舉例來說,在資料表中新增欄位通常都很安全,但新增控制代碼會導致原始碼破壞,不僅對該資料表,也會發生連帶包含該資料表的所有類型。

RFC-0149:非強制性的 FIDL 編碼驗證同樣適用本主題。這項工具會剖析可能發生的破壞事件類別,並決定是否對繫結執行編碼端驗證。如此一來,就能更精細地探討編碼端驗證的成本優缺點,以及遠處故障的風險。

單字語法、慣用樣式

我們沒有嚴格遵循「一種方法來執行」的理念。如果擔心使用者會浪費時間在決定瑣碎的替代方案時,我們會在 fidl-lintfidl-format (而非 fidlc) 中導入限制。

例如,FIDL 會接受以任何順序加入修飾符關鍵字,但我們希望在 Linter 中強制執行一致的順序。

再舉一個例子,RFC-0040:ID 唯一性透過在兩個 ID 具有相同標準格式的情況下,透過 fidlc 回報 ID 在轉換後會發生衝突的問題。較簡單的替代方案是在編譯器中強制執行 FIDL 命名慣例。然而,這個步驟會間隔太遠。基於正當理由使用不同的命名樣式 (例如說明核心 API),我們強烈建議使用 snake_case 方法。

標準化表示法

來自 RFC-0131:FIDL 線路格式的設計原則

FIDL 值必須有一個明確且不明確的表示法,即只有一個 FIDL 值的編碼表示法和一個 FIDL 值的解碼表示法。

FIDL 線格式是標準:特定訊息只有一個編碼。做為共同項目,每個位元組都會列入計算:在不變更訊息含義的情況下,沒有可供變更的位元組。

例如,規格要求所有邊框間距位元組全都是零。同樣地,RFC-0047: Tables 會禁止儲存多餘的空白信封,以確保標準表示法。

標準表示法可讓 FIDL 更加簡便且安全。舉例來說,如果允許非零填充,可能會導致 FIDL 訊息外洩,導致記憶體中佔用這些位元組的機密資訊。允許特定訊息進行多個表示法,也會導致很少執行的程式碼路徑可能隱藏錯誤,例如「額外空白信封」程式碼路徑。標準表示法也能讓您在不瞭解結構定義的情況下,輕鬆比較訊息的相等性:對於「值類型」,這是簡單的 memcmp

無須分配

來自 RFC-0131:FIDL 線路格式的設計原則

系統可以在單次傳遞中編碼及解碼,而不配置超出堆疊空間 (即沒有動態堆積配置) 的配置。

這項要求大幅影響傳輸格式的設計:必須能夠只使用堆疊來進行解碼。因此,線路格式是使用狀態指標和深度優先遍歷順序,而非諸如偏移式格式,需要輔助資料結構才能在解碼時持續追蹤資訊。

這個原則與「用多少付多少」的計費方式相關,因為這個原則適用於非常低階的 FIDL 用途 (其中 malloc 可能尚未存在,或費用較低)。

沒有立即可用的反射功能

來自 RFC-0131:FIDL 線路格式的設計原則

如未明確選擇加入,就不得允許對等點對通訊協定、公開方法或公開類型執行反射。

交通通用

雖然首先是二進位線格式,但這並不表示 FIDL 應與 Zircon 管道傳輸緊密耦合。還有其他需要考量的重要用途,例如說明核心 API、處理中訊息傳遞和持續性。

RFC-0050:語法 revamp 說明傳輸一般化的未來方向。

RFC-0062:方法不可能遭部分拒絕,因為 FIDL 與 Zircon 管道傳輸太過緊密。