为了更好地理解Never 和Void,以及Never 如何在比旧的@noreturn 更多的上下文中有用,让我们首先看看这两种类型的实际定义是什么:
Never 定义为 here:
public enum Never {}
由于无法实例化空枚举的值,类型系统保证不存在Never 的实例。这意味着将其返回类型指定为 Never 的函数在任何情况下都会被类型系统阻止实际返回。
编译器在进行控制流分析时会考虑到这一点。例如,这两个函数都编译没有错误,而如果将返回 Void 的函数替换为 fatalError,它们将失败:
func foo(fail: Bool) -> String {
if fail {
fatalError()
} else {
return "foo"
}
// notice there is no return statement here
}
func bar(fail: Bool) -> Void {
let s: String
if fail {
fatalError()
// the compiler doesn't complain s is not initialized here
} else {
s = "bar"
}
print(s)
}
Void 定义为 here:
public typealias Void = ()
空元组没有两个不同的实例。因此,返回Void 的函数的返回值不包含任何信息。
您实际上可以写return () 或return Void()。您也可以使用返回的“值”,如下所示:
func empty() -> Void {}
let v = empty()
print(type(of: v)) // prints "()"
尽管编译器会警告“Constant 'v' inferred to have type 'Void', which may be unexpected”。
根据类型系统而不是特殊的语言特性来定义Never 和Void 使我们能够用泛型做一些非常聪明的事情。让我们看一个 Result 类型的示例,它对成功和失败类型都是通用的。
enum Result<R, E> {
case success(R)
case failure(E)
}
可能的特化是Result<Void, MyError>。这意味着您有一个结果,在成功时,除了它成功的事实之外不包含任何信息。
另一种可能是Result<String, Never>。编译器保证这个结果永远不会失败。
Optional 以类似的方式与 Never 和 Void 交互。 Never? 只能是 nil,Void? 只保存它是否为 nil 的信息,仅此而已(它基本上是一个更复杂的 Bool)。这两者本身并不是很有用,但当Never 或Void 在某处用作泛型参数时可能会出现。
实际上,您很少会编写返回Never 的函数。我个人用它来包装fatalError 来创建一个我用来标记尚未实现的函数的函数:
func unimplemented(f: String = #function) -> Never {
fatalError("\(f) is not implemented yet")
}
另一个返回Never 的函数示例是dispatchMain(),它可以在命令行实用程序中用于启动DispatchQueue.main。由于这个队列会等待新的块,dispatchMain() 永远不会返回。