05 Generics¶
此部分介绍 YIAN 中的泛型系统, 包括泛型函数/方法, 泛型类型, 以及相关的概念和语法.
YIAN 的泛型是纯静态的, 也就是说, 泛型参数必须在编译时被具体化为一个具体的类型, 从而生成对应的函数/方法/类型实例.
Generic Structs/Enums¶
一个泛型 struct 和 enum 的实例如下:
struct Point<T> {
T x
T y
}
enum Option<T> {
None
Some { T val }
}
实例化这些类型时, 不仅需要提供类型名称, 还需要指定类型参数:
Point<i32> p = Point<i32>(1, 2)
Option<i32> o = Option<i32>.Some(42)
唯一的例外是构造泛型 struct 时, 可以省略类型参数, 由编译器根据构造函数参数的类型推断出类型参数:
Point<i32> p = Point(1, 2) // 编译器推断出 T 是 i32
Generic Traits¶
一个泛型 trait 的实例如下:
trait Addable<T> {
T add(T other)
}
注意: 其中包含的方法 add 也是一个泛型方法, 因为它的返回类型和参数类型都依赖于 trait 的类型参数 T.
通过 impl 块可以将一个泛型 trait 实例化, 并为特定类型实现该 trait:
impl Addable<i32> for i32 {
i32 add(i32 other) {
return *self + other
}
}
Generic Functions¶
一个泛型函数的实例如下:
T identity<T>(T x) {
return x
}
在函数名后指定了类型参数之后, 函数的返回值, 参数列表, 以及函数体都会处于泛型上下文中, 在这个上下文中, 可以将类型参数当作一个普通的类型来使用. 泛型函数在被调用时, 会根据调用处指定的类型实参来生成对应的函数实例:
i32 a = identity<i32>(42) // 调用 identity 函数, 指定 T 为 i32
str s = identity("Hello") // 调用 identity 函数, 省略类型参数, 由编译器推断出 T 是 str
Generic Methods¶
泛型方法的定义比较特殊, 简单来说, 如果一个方法满足下列其中一个:
- 自身包含类型参数
- 定义在一个泛型上下文中
那么它就是一个泛型方法. 例如, 在Generic Traits章节中定义的 add 方法就是一个泛型方法, 因为它定义在一个泛型 trait 中.
再例如:
impl str {
T parse<T>() {
T.from_str(*self)
}
}
在这个例子中, parse 方法是一个泛型方法, 因为它自身包含类型参数 T.
泛型方法在被调用时, 需要进行实例化, 但是由于泛型方法的类型参数既可以来自于方法自身, 也可以来自于所在的泛型上下文, 因此在调用时, 两种类型参数确定的方法是不同的:
- 对于方法自身的类型参数: 在调用时, 需要显式指定类型实参, 例如
s.parse<i32>(), 也可以省略类型实参, 由编译器根据调用上下文推断出类型实参(前提是参数的信息足够) - 对于泛型上下文中的类型参数: 在调用时, 根据方法的调用者确定, 不需要显式指定
Generic Impls¶
泛型 impl 是指拥有自己的类型参数的 impl 块, 例如:
impl<T> Point<T> {
T x() {
return self.x
}
}
在这个例子中, impl 块拥有一个类型参数 T, 从而创建了一个泛型上下文, 这个 impl 块的目标类型 Point<T> 就是使用上下文中的 T 类型实例化出来的. 对于 impl for 的语法同理.
泛型 impl 不仅仅为某个特定的类型实现方法, 而是为一个类型族实现方法. 在上面的例子中, 这个类型族就是能够从 Point<T> 实例化出来的所有类型, 例如 Point<i32>, Point<f64>, Point<str> 等等. 下面是一个更复杂的例子:
trait Summable<T> {
T sum()
}
struct Pair<T, U> {
T first
U second
}
impl<T> Summable<T> for Pair<T, T> {
T sum() {
return self.first + self.second
}
}
在这个例子中, impl 块为 Pair<T, T> 这个类型族实现了 Summable<T> trait, 也就是说, 只要 Pair 的两个类型参数相同, 就可以调用 sum 方法来计算它们的和:
Pair<i32, i32> p1 = Pair(1, 2)
i32 result1 = p1.sum() // 调用 sum 方法, 结果是 3
Pair<i32, f64> p2 = Pair(1, 2.0)
// i32 result2 = p2.sum() // 编译错误, 因为 Pair<i32, f64> 不满足 Pair<T, T> 的条件