介绍
SE-0361,在 Swift5.7 已经实现。
目前指定泛型的类型参数基本都是通过<>
来表示,例如Array<String>
。但是Extension
是个例外,因为如果你想为了某个泛型添加Extension
, 并且指定参数类型,使用<>
指定泛型约束的类型如String
,此时编译器会报错,例如:
extension Array<String> { ... } ❌ // error: Constrained extension must be declared on the unspecialized generic type 'Array' with constraints specified by a 'where' clause
此时,编译器会告诉你使用where
子句来为extension
添加受约束的类型,例如:
extension Array where Element == String { ... } ✅
本篇提议的目的就是为了移除extension
上对泛型参数声明的限制,允许通过<>
来声明约束的泛型参数。
提议动机
在 Swift 语言中,基本到处可见使用<>
在泛型类型名称后面声明绑定的泛型类型。例如,我们可以声明一个别名StringArray
, 这个别名代表Array<String>
类型,然后我们再对别名进行扩展:
typealias StringArray = Array<String>
extension StringArray { ... }
结合0346, 我们还可以为协议声明一个主要关联类型,并且使用<>
绑定到扩展声明上,例如:
protocol Collection<Element> {
associatedtype Element
}
extension Collection<String> { ... }
但是编译器不允许直接在扩展上使用这个语法绑定泛型类型,这种限制确实很让开发者困惑。
代码语言:Swift复制extension Array<String> { ... } // error: Constrained extension must be declared on the unspecialized generic type 'Array' with constraints specified by a 'where' clause
所以本篇提议是为了去掉这个限制,任何地方为泛型类型约束参数类型都可以使用<typename>
方式。
提议解决方案
本篇文章提议使用<>
绑定参数类型来扩展绑定泛型类型,或者使用语法糖[String]
和Int?
。
以String
为例子,如果要为元素为String
类型的数组进行扩展,我们有以下三种方式可以声明:
/// 1. 最原始方式
extension Array where Element == String { ... }
/// 2. 尖括号方式
extension Array<String> { ... }
/// 3. 语法糖方式
extension [String] { ... }
设计细节
扩展的泛型类型名称可以由尖括号中,
隔开的类型参数列表组成。 类型参数列表将泛型类型的类型参数绑定到每个指定的类型参数。这种写法最终跟where
子句表达等价。例如:
struct Pair<T, U> {}
extension Pair<Int, String> {}
extension 与下面where
语句等价:
extension Pair where T == Int, U == String {}
设计需要遵循几个规则:
- 对泛型类型扩展,它的类型参数列表在扩展时,必须指定所有的类型参数。如果在扩展中只约束类型参数中的一个,此时还需要使用
where
子句。例如:
struct Pair <T, U> {}
extension Pair<Int> {} // ❌ error: Generic type 'Pair' specialized with too few type parameters (got 1, but expected 2)
extension Pair where T == Int {} // ✅
- 指定的参数必须是明确的类型,不能在扩展时使用默认占位符
_
来代替参数:
extension Pair<Int, _> {} // ❌ error: Cannot extend a type that contains placeholders
理由:当使用
_
来代替参数时,它会让编译器根据上下文推断默认参数的类型,这个类型也就变得不受约束,而且Pair<Int, _>
在不同的上下文,也意味不同的表达,完全不受控制。
- 类型参数的查找是在扩展上下文之外进行的,所以泛型类型的参数是不能出现类型参数列表中。因为泛型类型的参数在上下文中,无法代表一个准确的类型。比如
Element
。
extension Array<Element> {} // error: Cannot find type 'Element' in scope
- 如果用于扩展的泛型支持语法糖,那么扩展时同样支持语法糖,比如
[String]
:
extension [String] { ... } // Extends Array<String>
extension String? { ... } // Extends Optional<String>
总结
- Swift5.7 泛型类型支持带尖括号的扩展绑定
- 至此,包括 extension, Swift 均支持带
<>
扩展语法