Swift5.7: 泛型类型支持带尖括号的扩展绑定

2022-12-01 12:51:18 浏览数 (1)

介绍

SE-0361,在 Swift5.7 已经实现。

目前指定泛型的类型参数基本都是通过<>来表示,例如Array<String>。但是Extension是个例外,因为如果你想为了某个泛型添加Extension, 并且指定参数类型,使用<>指定泛型约束的类型如String,此时编译器会报错,例如:

代码语言:Swift复制
extension Array<String> { ... } ❌ // error: Constrained extension must be declared on the unspecialized generic type 'Array' with constraints specified by a 'where' clause

此时,编译器会告诉你使用where子句来为extension添加受约束的类型,例如:

代码语言:Swift复制
extension Array where Element == String {  ...  } ✅

本篇提议的目的就是为了移除extension上对泛型参数声明的限制,允许通过<>来声明约束的泛型参数。

提议动机

在 Swift 语言中,基本到处可见使用<>在泛型类型名称后面声明绑定的泛型类型。例如,我们可以声明一个别名StringArray, 这个别名代表Array<String>类型,然后我们再对别名进行扩展:

代码语言:Swift复制
typealias StringArray = Array<String>
extension StringArray { ... }

结合0346, 我们还可以为协议声明一个主要关联类型,并且使用<>绑定到扩展声明上,例如:

代码语言:Swift复制
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类型的数组进行扩展,我们有以下三种方式可以声明:

代码语言:Swift复制
/// 1. 最原始方式
extension Array where Element == String {  ... }
/// 2. 尖括号方式
extension Array<String> { ... }
/// 3. 语法糖方式
extension [String] { ... }

设计细节

扩展的泛型类型名称可以由尖括号中,隔开的类型参数列表组成。 类型参数列表将泛型类型的类型参数绑定到每个指定的类型参数。这种写法最终跟where子句表达等价。例如:

代码语言:Swift复制
struct Pair<T, U> {}

extension Pair<Int, String> {}

extension 与下面where语句等价:

代码语言:Swift复制
extension Pair where T == Int, U == String {}

设计需要遵循几个规则:

  • 对泛型类型扩展,它的类型参数列表在扩展时,必须指定所有的类型参数。如果在扩展中只约束类型参数中的一个,此时还需要使用where子句。例如:
代码语言:Swift复制
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 {} // ✅
  • 指定的参数必须是明确的类型,不能在扩展时使用默认占位符_来代替参数:
代码语言:Swift复制
extension Pair<Int, _> {} // ❌ error: Cannot extend a type that contains placeholders

理由:当使用_来代替参数时,它会让编译器根据上下文推断默认参数的类型,这个类型也就变得不受约束,而且Pair<Int, _>在不同的上下文,也意味不同的表达,完全不受控制。

  • 类型参数的查找是在扩展上下文之外进行的,所以泛型类型的参数是不能出现类型参数列表中。因为泛型类型的参数在上下文中,无法代表一个准确的类型。比如Element
代码语言:Swift复制
extension Array<Element> {} // error: Cannot find type 'Element' in scope
  • 如果用于扩展的泛型支持语法糖,那么扩展时同样支持语法糖,比如[String]
代码语言:Swift复制
extension [String] { ... } // Extends Array<String>

extension String? { ... } // Extends Optional<String>

总结

  1. Swift5.7 泛型类型支持带尖括号的扩展绑定
  2. 至此,包括 extension, Swift 均支持带<>扩展语法

0 人点赞