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 完整定義或審查泛型類型。在未來類型泛型可用的情況下,這門語言將提供更佳且更正式的方式,用於描述可參數化的類型和類型別名。
- 對於泛型如何轉譯為不支援泛型的語言 (例如 Go 1、C),FIDL 語言尚未做出正式決定。
- 在目前的語法中,您可能需要隱含使用類型參數,才能建立完整的類型。此外,如何剖析及設定巢狀部分型別別名 (即
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>; |
荒漠油廠 | type SmallBytes = Vec<u8>; |
Dart 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) 其他運算子 ([] 、* 、-> 等)。 |
荒漠油廠 | 新類型可轉譯為單例結構體,從中衍生出屬性,例如基礎類型實作的 From<UnderlyingType> 、Into<UnderlyingType> 、Hash 、PartialEq 、Copy 、Clone 、std::ops::* 等。 |
Dart | 根據與 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_;
};
荒漠油廠
#[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...