返回类型Never
的func
做什么?
例如:
func addNums() -> Never {
//my code
}
如果我像这样保持返回类型Void
会有什么区别?
func addNums() -> Void {
//my code
}
假设我希望处理一个fatalError
(如dpassage所说);下面的代码就足够了:
print("its an error")
return
苹果文档说:
不正常返回的函数的返回类型,即没有值的类型。
来源: 开发商
这不是一个关于何时以及如何在 Swift 中使用@noreturn属性的重复问题?,因为我希望得到一个更详细的答案,需要以下详细信息:
关于作为返回类型的
Never
和Void
之间的差异的实际示例我们应该采用这些返回类型的条件。
此外,返回类型也可能为零;我也需要比较该功能
答案应该集中在差异上。
Swift 3 中引入了Never
返回类型来替换@noreturn
键。
请参阅此提案中的理由:
SE-0102 删除@noreturn属性并引入空的 Never 类型
正如官方文档所解释的那样:
不正常返回的函数的返回类型;具有 没有值。
声明闭包时使用 Never 作为返回类型, 函数,或无条件抛出错误的方法、陷阱或 否则不会终止。
来源: https://developer.apple.com/documentation/swift/never
基本说明:
// The following function is our custom function we would use
// to manually and purposefully trigger crash. In the logs,
// we can specify what exactly went wrong: e.g. couldn't cast something,
// couldn't call something or some value doesn't exist:
func crashApp() -> Never {
fatalError("Something very, very bad happened! Crash the app!")
}
使用细节和优于@noreturn
的优势,如Erica Sadun所引用的:
- 从不允许函数或方法抛出:例如 () 抛出 ->从不。抛出允许使用辅助路径进行错误修正,即使在预期不会返回的函数中也是如此。
- 作为第一类类型,Never 以 @noreturn 属性无法处理的方式处理泛型。
- 从不主动阻止函数同时声明返回类型和不返回。这是旧制度下的一个潜在问题。
第一个注意事项(关于辅助错误修复)可能特别重要。Never
函数可以具有复杂的逻辑和抛出 - 不一定崩溃。
让我们看看一些有趣的用例以及Never
和Void
之间的比较
从不
例 1
func noReturn() -> Never {
fatalError() // fatalError also returns Never, so no need to `return`
}
func pickPositiveNumber(below limit: Int) -> Int {
guard limit >= 1 else {
noReturn()
// No need to exit guarded scope after noReturn
}
return rand(limit)
}
例 2
func foo() {
abort()
print("Should not reach here") // Warning for this line
}
例 3
func bar() -> Int {
if true {
abort() // No warning and no compiler error, because abort() terminates it.
} else {
return 1
}
}
abort()
定义为:
public func abort() -> Never
无效
这些例子在返回Void
的情况下是不可能的:
public func abortVoid() -> Void {
fatalError()
}
func bar() -> Int {
if true {
abortVoid() // ERROR: Missing return in a function expected to return 'Int'
} else {
return 1
}
}
并用abort()
返回Never
打包:
func bar() -> Int {
if true {
abort() // No ERROR, but compiler sees it returns Never and warns:
return 2 // Will never be executed
} else {
return 1
}
}
我们使用Void
告诉编译器没有返回值。应用程序保持运行。
我们使用Never
告诉编译器不会返回到调用方站点。应用程序运行循环终止。
Void
Void 本身是一个返回类型,它是一个具有零元素的元组。您可以互换使用 Void 和 ()。
看看这些例子,
-
func yourFunc() {}
这是一个没有返回类型的函数,它基本上返回一个零元素的元组,可以写成() -
func yourFunc() -> Void {}
显式通知编译器返回 void 类型的函数 -
func yourFunc() -> () {}
此返回类型 () 显示与 void 类型相同。() 表示包含零个元素的元组
从不
从不返回类型通知编译器不需要返回空元组 ()。此外,具有永不返回类型的函数用于当前执行的退出点,如崩溃、致命错误、中止或退出。
为了详细了解never,让我们看一个 abort() 示例:
1.
func yourFunc() {
abort()
print("Will not reach at this point") //Warning for this line
}
阿拉伯数字。
func yourFunc() -> Int {
if true {
abort()
} else {
return 1
}
}
从上面的代码片段中,我们可以看到何时调用 abort()(它不返回值)作为期望返回值的函数中的最后一个语句(在我们的例子中为 Int)。编译器不会生成警告。
中止()
public func abort() -> Never
同样,对于 exit():
public func exit(_: Int32) -> Never
Apple 文档说:">在声明无条件引发错误、陷阱或以其他方式不终止的闭包、函数或方法时,请使用 Never 作为返回类型。
因此,如果要编写记录灾难性错误的自定义函数,则应使用返回类型 Never 向编译器发出信号:
func catastrophicErrorDisplay(error: String) -> Never {
DisplaySomeCustomLogFacility(error)
}
简而言之,">从不用于无法恢复的突然和完全故障。
为了更好地理解Never
和Void
,以及Never
在比旧@noreturn
更多的上下文中是如何有用的,让我们首先看看这两种类型实际上被定义为什么:
Never
在这里定义为:
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
在这里定义为:
public typealias Void = ()
空元组没有两个不同的实例。因此,返回Void
的函数的返回值不包含任何信息。
您实际上可以写return ()
或return Void()
.你也可以使用返回的"value",如下所示:
func empty() -> Void {}
let v = empty()
print(type(of: v)) // prints "()"
尽管编译器会警告"常量'v'推断为类型'Void',这可能是意料之外的"。
根据类型系统而不是特殊语言功能来定义Never
和Void
,使我们能够对泛型做一些非常聪明的事情。让我们看一个Result
类型的示例,该类型在成功和失败类型上都是通用的。
enum Result<R, E> {
case success(R)
case failure(E)
}
对此的可能专业化将是Result<Void, MyError>
.这意味着你有一个结果,在成功时,除了它成功的事实之外,没有任何信息。
另一种可能性可能是Result<String, Never>
.编译器保证此结果永远不会失败。
可选以类似的方式与Never
和Void
交互。Never?
只能是零,Void?
只保存信息,无论它是否为零,仅此而已(它基本上是一个更复杂的布尔值)。这两者本身都不是很有用,但当Never
或Void
在某处用作泛型参数时可能会出现。
在实践中,您很少会编写返回Never
的函数。我个人用它来包装fatalError
创建一个函数,我用它来标记尚未实现的函数:
func unimplemented(f: String = #function) -> Never {
fatalError("(f) is not implemented yet")
}
函数返回Never
的另一个例子是dispatchMain()
,它可以在命令行实用程序中用于启动DispatchQueue.main
。由于此队列随后等待新块,因此dispatchMain()
永远不会返回。
Never
表示该函数永远不会返回。它旨在用于fatalError
等导致程序故意崩溃的事情,通常是在记录错误之后。您可能不应该使用它,除非您正在执行诸如为应用程序中的灾难性错误创建处理程序之类的操作。
这与不返回值的函数不同,就像在第二个代码段中一样。你也可以把它写成func addNums() -> Void
.