| RFC-0052:型別別名和新類型 | |
|---|---|
| 狀態 | 已接受 |
| 區域 |
|
| 說明 | 這項提案旨在正式演進 FIDL 的型別別名和新式型別宣告機制,以及這些機制對繫結的影響。具體來說,這項提案建議對型別別名文法進行改良、將型別別名公開給繫結,以及推出稱為「新類型」的新功能。 |
| 作者 | |
| 提交日期 (年-月-日) | 2020-01-07 |
| 審查日期 (年-月-日) | 2020-01-23 |
「Call me by another name」(幫我取另一個名字)
摘要
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 目前支援部分型別參照 (如範例所示),但這項功能將會移除。SmallVec
using淘汰這項功能的理由有三:
- FIDL 的型別泛型尚未完全定義或審查。在未來有型別泛型的語言中,描述可參數化的型別和型別別名時,會更完善且正式。
- FIDL 語言尚未正式決定如何將泛型轉換為不支援泛型的語言 (例如 Go 1、C)。
- 目前的語法會建立情境,其中可能隱含需要型別參數,才能建立完整型別。此外,如何剖析及參數化巢狀偏好類型別名 (即
using SmallVecOfVecs = vector<vector:SMALL_NUM>:SMALL_NUM) 的具體細節也不清楚。
系統不支援 protocol 的別名,因為無論是在 FIDL 或繫結語言中,protocol 都無法完全歸類為型別。如果通訊協定日後變得更像型別 (即 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...
-
Go 將在不久的將來支援泛型。↩
-
在 Dart 導入非函式型別的 typedefs (#65) 之前,↩