我在链中使用 Swift 的nil-coalescing运算符来打印三个值之一。前两个是可选的,最后一个是非可选的:
let optionalString: String? = nil
let optionalError: Error? = MyError.anError
let defaultString: String = "default"
print(optionalString ?? (optionalError ?? defaultString))
我很惊讶看到这段代码打印出来
Optional(MyError.anError)
到控制台。我期待一个非可选的项目:
MyError.anError
错误定义如下:
enum MyError: Error {
case anError
}
我知道nil-coalescing运算符有两种实现方式:
func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T
和
func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T?) rethrows -> T?
因此,??
应该返回可选的(T?
)仅当defaultValue
也是可选的(T?
) 。但是事实并非如此:
第一个??
运算符的默认值为(optionalError ?? defaultString)
。要评估此值的类型,我们需要评估第二个??
运算符的结果。由于defaultString
的类型为String
,因此是非可选的,因此函数(1)用于nil-coalescing运算符。因此,(optionalError ?? defaultString)
的结果也必须是非可选的,这是第一个??
运算符的默认值。同样,函数(1)用于nil-coalescing运算符,因此optionalString ?? (optionalError ?? defaultString)
应该返回* non- *可选。
显然,我的逻辑存在缺陷,但我看不到。有什么解释吗?
答案 0 :(得分:1)
免责声明:我不确定是否应该将其添加为问题的补充或是否应将其发布为答案。我决定为后者。这不是防弹的解释,但我认为对于许多可能遇到相同问题的人来说,它“足够接近”。
以下两条注释指出所提供的代码无法为注释程序编译,这将我推向正确的方向:
我将代码复制到一个空白的操场上并运行它。对我来说,它是 did 编译的,但是它还显示了一个警告消息,它在之前没有显示(由于某些原因,原始项目中的自动完成和编译器警告已损坏)。
警告
表达从“任何吗?”隐式强制到“任何”
告诉我表达式(optionalError ?? defaultString)
显然被评估为类型Any?
,这是可选的。然后,编译器选择??
运算符(1)来评估整个表达式,这要求其默认值(optionalError ?? defaultString)
是非可选的。因此,该值从Any?
隐式强制为Any
。这就是打印Optional(MyError.anError)
的原因。
还有两个问题:
(optionalError ?? defaultString)
被评估为Any?
?我没有问题1 ,
的答案但是我的猜测是,当参数类型不匹配并且不清楚要使用哪个函数时,编译器会简单地为函数(1)分配更高的优先级。 (我很高兴得到纠正。)
关于问题2 :
optionalError
的类型为MyError?
,而defaultString
的类型为String
。 ??
运算符的签名为:
func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T
func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T?) rethrows -> T?
因此,optional
和defaultValue
这两个参数必须具有“相同”的泛型类型(仅在第一种情况下,一个参数是该特定类型的可选参数,而另一个则不是) 。 MyError
和String
显然不是同一类型。因此,为了使其与函数签名的条件匹配,编译器必须将两种类型都强制转换为Any
。
因此,MyError?
被强制转换为Any?
,显然,String
也被强制转换为Any?
,因此,使用函数(2)进行求值(optionalError ?? defaultString )to
好吗?
这又引发了一个问题,为什么在这种情况下使用函数(2),这与我对问题(1)的假设相矛盾,但是我的主要结论是,使用两个函数简直是一个坏主意nil-coalescing运算符设置不同的类型。
我从RxSwift的一本教科书中获得了此代码示例,该示例略有不同,因为它使用的是必须符合CustomStringConvertible
的通用类型:
func print<T: CustomStringConvertible>(label: String, event: Event<T>) {
print(label, event.element ?? event.error ?? event)
}
首先让我尝试一下的是,当我从书中运行代码时,它打印了一个可选内容,而书中声称它将打印一个非可选内容。