RFC-0052:类型别名和新类型 | |
---|---|
状态 | 已接受 |
领域 |
|
说明 | 此提案旨在正式改进 FIDL 的类型别名和新的类型声明机制及其对绑定的影响。更具体地说,这对类型别名语法、向绑定公开类型别名以及一项称为新类型的新功能提出了建议。 |
作者 | |
提交日期(年-月-日) | 2020-01-07 |
审核日期(年-月-日) | 2020-01-23 |
“用其他名字给我打电话”
摘要
部分 RFC-0019 在 FIDL 中引入了类型别名的概念。 此提案旨在正式改进 FIDL 的类型别名和新类型 声明机制及其对绑定的影响。更具体地说, 对类型别名语法、类型别名 以及一项称为“新类型”的新功能
设计初衷
类型别名现已成为 FIDL 中的一项功能,此 RFC 旨在扩展 范围和实用性使用类型别名时,声明将产生影响 因此有助于 API 名称转换。
除了类型别名,此 RFC 还引入了姊妹概念 - 新的 类型声明。新类型(类似于别名)会在本地 该作用域封装了另一种类型,但被视为不同于类型检查工具 (命名类型语义)。这将使 FIDL 作者能够更好地利用 其绑定语言的类型系统。
设计
类型别名
如需了解类型别名的完整上下文, RFC-0019 (使用样式别名建议当前的样式)突出显示原始设计, 以及优先顺序。此 RFC 旨在为公开披露声明的 输入语言绑定中的别名并优化一些语言语法 决策。与使用别名非常相似,新类型别名纯粹 是标称的,不会影响 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)。
- 当前语法会造成类型参数可能
创建完全成型的类型时为隐式类型。此外,这些 API 的
如何解析和参数化嵌套的部分类型别名(即
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>; |
Dart 2 | typedef SmallBytes = List<int>; |
Go | type SmallBytes = []uint8; |
新类型
新类型是指封装了基础类型但被视为不同的类型
从语言类型系统中的基本类型中提取出来。如果有两个条件,
值具有相同的字号和形状(可能还具备特性),
具有不同的语义含义。例如,zx_vaddr_t
和
zx_paddr_t
由相同的底层 uintptr_t
类型表示,但具有
不同(但容易混淆)的含义。
一种新类型能够以类型中表示这些语义差异 系统。在强类型语言中,这意味着系统可能会捕获逻辑 bug, 在编译时通过类型检查和/或静态分析来实现。
就传输格式而言,这不会产生任何变化,也不会产生任何费用。
语法
newtype-declaration = ( attribute-list ), "type", IDENTIFIER, "=", type-constructor;
选择此语法是为了与顶级类型的引入(如 (如 RFC-0050:语法改版)。
由于新类型应转换为绑定中的具体类型,
type-constructor
应解析为完整的类型引用。这可能会
如下所示:
type MyBytes = vector<uint8>:MAX;
protocol
无法完全支持新的 protocol
类型
无论是以 FIDL 还是绑定语言来分类的。此外,还有
对于由 protocol
衍生的命名类型语义,目前没有令人信服的用例
实体。
类型 trait 和运算符
如果没有新类型,只需使用
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 | 根据与 Dart 团队的对话,目前尚无法将新的类型语义映射到语言,但已经讨论过支持此类用例3。 新类型可降级为类型别名,等待第 65 条 [^2] 进行讨论。 |
Go | 新类型会直接映射到类型定义。type <NewType> <UnderlyingType> |
因此,底层类型的固有功能集称为 类型特征(尚未在 语言)。
除非/直到在 FIDL 中定义了类型特征,否则新类型应继承 即底层类型的 trait。这样一来,新类型 与底层类型相同,无需解封 而且可能会撤消新类型在类型安全方面的优势。 在将来,能够定义 这种新类型的设计不会阻止向此类 规划路径。不过,默认情况下,特征继承确实使 选择接受特征继承的迁移路径,这是一项重大的破坏性更改。
示例
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...