RFC-0052:類型別名和新類型 | |
---|---|
狀態 | 已接受 |
區域 |
|
說明 | 本提案旨在正式改進 FIDL 的類型別名和新的型別宣告機制,以及對繫結的影響。更具體來說,這會針對類型別名文法修正、將型別別名呈現在繫結方面,以及稱為「新型別」的新功能。 |
作者 | |
提交日期 (年/月) | 2020-01-07 |
審查日期 (年/月) | 2020-01-23 |
「其他名字打電話給我」
摘要
RFC-0019 的部分會在 FIDL 中導入類型別名的概念。本提案旨在正式改進 FIDL 的類型別名和新的類型宣告機制,以及它們對繫結的影響。更具體而言,這會針對類型別名文法修正、將類型別名導入繫結,以及稱為「新類型」的新功能。
提振精神
目前 FIDL 中已有類型別名的功能,而這個 RFC 旨在擴大其範圍和實用性。使用類型別名時,宣告會對繫結造成影響,因此對於 API 名稱轉換作業很有幫助。
除了輸入別名外,這個 RFC 也導入了姊妹概念,也就是新的類型宣告。新類型與別名非常類似,是在本機範圍內宣告新名稱,此名稱包裝另一個類型,但會被視為類型檢查工具 (已命名的類型語意) 不同。這樣一來,FIDL 作者就能更有效地運用其繫結語言的類型系統。
設計
類型別名
如需類型別名的完整背景資訊,RFC-0019 (建議使用樣式別名提供目前) 會醒目顯示原始設計、動機和優先順序。這個 RFC 的目標是鋪滿路徑,以在語言繫結中公開類型別名,並修正 RFC-0019 中的部分語言文法決策。與使用別名類似,新的類型別名純粹是常規,不會影響 ABI。本節主要會概略說明原始功能的變更和改善項目。
文法
alias-declaration = ( attribute-list ) , "alias" , IDENTIFIER , "=" , type-constructor;
type-constructor
應解析為格式完整且存在於目前範圍內的類型參照。換句話說,如果是 vector
這類類型,類型必須包含內部類型和大小限制。
舉例來說,這個值會是有效的:
alias SmallBytes = vector<uint8>:SMALL_NUM;
但這並非 (在等號右側進行部分型別參考):
alias SmallVec = vector:SMALL_NUM;
雖然目前 FIDL 支援使用 using
別名的部分類型參照 (如 SmallVec
範例所示),但系統將移除這項功能。淘汰這項機制的理由有三大:
- 您尚未針對 FIDL 完整定義或審查類型泛型。日後,一旦存在類型泛型,語言將能以更正式、更正式的方式描述可參數化的類型和類型別名。
- FIDL 語言尚未正式決定,一般人如何將泛型翻譯成不支援泛型的語言 (例如 Go 1、C)。
- 目前的語法會建立各種情境,其中可能「間接」需要建立類型參數,才能建立完整格式。此外,我們無法剖析如何剖析巢狀部分類型別名,並將這類別名參數化 (即
using SmallVecOfVecs = vector<vector:SMALL_NUM>:SMALL_NUM
)。
系統不支援 protocol
的別名,因為 protocol
無法完整分類為 FIDL 或繫結語言中的類型。目標是將來是否與通訊協定更類似 (即 client_end:ProtocolName
和 server_end:ProtocolName
) 一樣,重新審視這項決定。
範例
語言 | 程式碼 |
---|---|
FIDL | alias SmallBytes = vector<uint8>:SMALL_NUM; |
C | (簡單的 C 繫結中不存在向量)typedef small_bytes uint8_t*; |
C++ | using SmallBytes = std::vector<uint8_t>; |
Rust | type SmallBytes = Vec<u8>; |
飛鏢 2 | typedef SmallBytes = List<int>; |
查看 | type SmallBytes = []uint8; |
新類型
新類型可納入基礎類型,但會視為與語言類型系統的基礎類型不同。如果兩個值具有相同的類型大小和形狀 (可能具有特性),但有不同的「語意」意義,這項功能就非常實用。舉例來說,zx_vaddr_t
和 zx_paddr_t
是以相同的基礎 uintptr_t
類型表示,但含義不同 (但很容易取得) 的含義。
新類型可讓您在類型系統中表示這些語意差異。在強型語言中,這意味著在編譯期間透過類型檢查和/或靜態分析找出邏輯錯誤。
電線格式並無任何變化,也不必支付任何費用。
文法
newtype-declaration = ( attribute-list ), "type", IDENTIFIER, "=", type-constructor;
之所以選擇這個語法,是為了配合 RFC-0050:語法修訂
由於新類型應轉換為繫結中的具體類型,因此 type-constructor
應解析為完整格式的類型參照。如下所示:
type MyBytes = vector<uint8>:MAX;
系統不會支援新的 protocol
類型,因為 protocol
無法完整分類為 FIDL 或繫結語言中的類型。此外,對於產生的繫結中 protocol
衍生實體的已命名類型語意目前沒有吸引人的用途。
類型特徵和運算子
如果沒有新的類型,在 FIDL 中使用新的單一欄位 struct
類型,大致可以完成新類型的效果。例如:
struct MyBytes {
vector<uint8>:MAX value;
};
不過,新類型會指示後端並繫結較小的語意空間,並允許他們產生語言專屬功能,以便對應至原生語言功能,或讓新類型符合人體工學使用。例如:
語言 | 說明 |
---|---|
C | 很抱歉,C 的型別系統沒有良好的呈現方式。備用至 typedef 。 |
C++ | 新類型可轉譯為 class ,以公開基礎類型提供的下列功能:(1) 明確的轉換建構函式、(2) 明確指定運算子、(3) 算術與位元運算,以及 (4) 其他運算子 ([] 、* 、-> 等)。 |
Rust | 新類型可轉譯為衍生特徵的單例結構,例如基礎類型實作的 From<UnderlyingType> 、Into<UnderlyingType> 、Hash 、PartialEq 、Copy 、Clone 、std::ops::* 等。 |
飛鏢 | 從與 Dart 團隊對話中,目前無法將新的類型語意對應至語言,但已討論相關用途 3。 新類型可降級為類型別名,待執行 #65 [^2] 後。 |
查看 | 新類型會直接對應至類型定義。type <NewType> <UnderlyingType> |
基礎類型的整合功能集稱為「類型特徵」 (一種語言尚未正式定義的概念)。
除非在 FIDL 中定義類型特徵,否則新類型預設會繼承基礎類型的特徵。這樣一來,新類型仍能照常使用,與基礎類型相同,無需解除基礎類型,而且可能會復原新類型的類型安全優勢。.不過根據預設,特徵繼承會讓遷移路徑朝選擇採用繼承,帶來大規模的破壞性變更。
範例
C++
// This example uses C++20 concepts for readability but this can be translated to a
// template approach in C++14.
template<typename T>
concept Addable = requires(T a) {
{ a + a } -> T;
};
template<typename T>
concept Multipliable = requires(T a) {
{ a * a } -> T;
};
template <typename T>
concept Copyable = std::is_copy_constructible_v<T> || std::is_copy_assignable_v<T>;
template <typename T>
concept Movable = std::is_move_constructible_v<T> || std::is_move_assignable_v<T>;
class MyNumber {
public:
using UnderlyingType = uint32_t;
explicit MyNumber() = default;
explicit MyNumber(const UnderlyingType& value)
requires Copyable<UnderlyingType>
: value_(value) {}
explicit MyNumber(UnderlyingType&& value) requires Movable<UnderlyingType>
: value_(std::move(value)) {}
MyNumber& operator=(const MyNumber&) = default;
MyNumber& operator=(MyNumber&&) = default;
[[nodiscard]] MyNumber operator+(const MyNumber& other) const
requires Addable<UnderlyingType> && Copyable<UnderlyingType> {
return MyNumber(value_ + other.value_);
}
[[nodiscard]] MyNumber operator+(MyNumber&& other)
requires Addable<UnderlyingType> && Movable<UnderlyingType> {
return MyNumber(value_ + other.value_);
}
[[nodiscard]] MyNumber operator*(const MyNumber& other) const
requires Multipliable<UnderlyingType> && Copyable<UnderlyingType> {
return MyNumber(value_ * other.value_);
}
[[nodiscard]] MyNumber operator*(MyNumber&& other)
requires Multipliable<UnderlyingType> {
return MyNumber(value_ + other.value_);
}
// ...other operators defined here...
[[nodiscard]] UnderlyingType value() const
requires Copyable<UnderlyingType> {
return value_;
}
UnderlyingType take_value() requires Movable<UnderlyingType> {
return std::move(value_);
}
private:
UnderlyingType value_;
};
Rust
#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
struct MyNumber(u32);
impl From<u32> for MyNumber {
fn from(value: u32) -> Self {
MyNumber(value)
}
}
impl Into<u32> for MyNumber {
fn into(self) -> u32 {
self.0
}
}
impl Add for MyNumber {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl Mul for MyNumber {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self(self.0 * rhs.0)
}
}
// ...implement other traits here...