介绍
当前提议主要是讲苹果在 Swift5.7 支持不透明结果类型的结构化表达,目前在 Swift5.7 已经实现。
不透明结果类型可以用作函数的结果类型,变量的类型和下标元素的结果类型。在这三种情况下,不透明结果类型必须是整个类型。比如用于函数的整个返回结果类型。本篇提议建议取消这种限制,并允许在“结构”位置使用不透明的结果类型。
目的
当前语法中对不透明结果类型的限制阻止了它们在许多常见的 API 模式中使用。可以看下面四个常见的例子:
代码语言:Swift复制// ❌,函数的不透明结果返回值有可能失败
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> {
var h = Hasher()
h.combine(t) // ❌ERROR - 'combine' 是 'Hashable'的实例方法,调用者泛型 'T'' 实必须实现 'Hashable' 协议
let _ = h.finalize()
return S(0)
}
但不透明结果类型没有类型推断的特性,例如把f
函数的返回值使用不透明结果类型H<some P>
表示,由于some
没有类型推断能力,T
无法根据上下文推断是否遵守Hashable
,此时f
函数会直接报错。例如:
// ❌,类型 'some P' 没有遵守协议 'Hashable'
func f<T>(_ t: T) -> H<some P> { /* ... */ }
对源代码兼容性的影响
新增特性,没有兼容性影响。
跟 SE-0244 中讨论的一样:
如果在库中采用不透明类型,则一开始会破坏源代码
[...]
兼容性, 因为不支持可变参数。但是由于向客户端暴露的细节很少,实际上可以对源代码和 ABI 稳定性来说长期受益。对源代码兼容性也有一些缓解措施,比如,原类型的弃用周期更长,或者用新的函数签名(返回的不透明结果类型)重载旧的函数签名(返回命名类型)。
对 ABI 稳定性的影响
新增特性,对 ABI 无影响。
对 API 扩展性的影响
新增特性,没有扩展性相关的影响。 SE-0244 提议已经说明:
不透明结果类型是函数的结果类型,变量类型,下标的元素类型,这三种类型的一部分。在不破坏API/ABI 稳定性的前提下,无法更改不透明结果类型的要求。但是,底层的具体类型可以在不破坏 ABI 的情况下从一个版本更改到下一个版本,因为 API 上层并不知道底层的具体类型。
Rust 的Impl Trait
特性
Swift 中的不透明结果类型是受 Rust 中的impl Trait
特性启发而来。SE-0244 中对比了some
和impl Trait
的异同点。其中一个不同点是impl Trait
允许在结构位置使用,这个特性与当前提议基本相同。impl Trait
与当前提议特性有个不同点,是impl Trait
不会出现在闭包特性或者函数指针的返回类型中。
总结
通过当前提议 SE-0328,你应当知道:
- 结果类型支持
(some P)?
和(some P)!
- 返回结果类型是函数类型时,支持
() -> (some P)