Swift5.7 支持结构化不透明结果类型
原创
介绍
当前提议主要是讲苹果在 Swift5.7 支持不透明结果类型的结构化表达,目前在 Swift5.7 已经实现。
不透明结果类型可以用作函数的结果类型,变量的类型和下标元素的结果类型。在这三种情况下,不透明结果类型必须是整个类型。比如用于函数的整个返回结果类型。本篇提议建议取消这种限制,并允许在“结构”位置使用不透明的结果类型。
目的
当前语法中对不透明结果类型的限制阻止了它们在许多常见的 API 模式中使用。可以看下面四个常见的例子:
// ❌,函数的不透明结果返回值有可能失败
func f0() -> (some P)? { /* ... */ }
// ❌,不能把不透明结果类型作为多个返回值中的一个,必须是对应单个且整个返回值
func f1() -> (some P, some Q) { /* ... */ }
// ❌,不能返回一个懒加载的计算 some 类型。(当 f2 调用完成且返回结果时,返回类型是`() -> some P`,此时返回值中并不确定 some 类型)
func f2() -> () -> some P { /* ... */ }
// ❌,不能把不透明结果类型嵌入到更大的结构中
func f3() -> S<some P> { /* ... */ }
上面四个调用示例都是之前的语法约定,如果解除这些限制,就可以使用不透明结果类型来表达更多 API 模式。所以我们应该允许在函数的结果类型、下标元素的类型和变量的类型,这三种类型的结构位置中使用不透明结果类型。
详细设计实现
可选语法
不透明结果类型的可选必须使用
(some P)?
表示,一个已经解包的不透明结果类型的可选必须使用
(some P)!
表示。
为什么不用
some P?
和
some P!
呢?
some P?
这种表达会被解释为
some Optional<P>
,由于不透明类型一定是
Any
,
AnyObject
, 组合的协议,或者基类中的一种,所以
some P?
这种表达一定错误。
some P!
也是同样的道理。
高阶函数
如果函数的结果类型、下标的结果类型和变量的类型是函数类型,那么该函数类型只能在返回位置包含结构不透明类型。例如,
func f() -> () -> some P
合法,但是
func g() -> (some P) -> ()
会直接报错,这里主要因为
some
不能出现在函数结果类型的参数位置:
protocol P {}
func g() -> (some P) -> () { ... } // 'some' 不能出现在 '(some P) -> ()' 的参数位置
约束推断能力
当泛型参数类型用在函数签名(可以简单理解为函数名加参数的唯一标识)的结构位置时,编译器会根据使用泛型参数的上下文来隐式约束泛型参数类型。例如下面例子中
f
函数中泛型参数会被推断为
Hashable
:
struct H<T: Hashable> { init(_ t: T) { } }
struct S<T>{ init(_ t: T) { } }
// 与 'f<T: Hashable>' 等价,因为返回值 'H<T>' 就是指 'T: Hashable'
func f<T>(_ t: T) -> H<T> {
var h = Hasher()
h.combine(t) // 可以编译通过, 这里推断知道 'T: Hashable'(combine 是 Hashble 的实例方法)
let _ = h.finalize()
return H(0)
// 'S<T>' 没有实现任何 'T' 相关的协议
func g<T>(_ t: T) -> S<T> {