什么是永不归类?

时间:2017-10-24 05:16:45

标签: swift types

返回类型为func的{​​{1}}有什么作用?

例如:

Never

如果我将返回类型保留为func addNums() -> Never { //my code } ,会有什么不同?

Void

假设我希望处理func addNums() -> Void { //my code } (如dpassage所述);以下代码就足够了:

fatalError

Apple文档说:

  

不能正常返回的函数的返回类型,即没有值的类型。

来源:Developer

这不是When and how to use @noreturn attribute in Swift?的重复问题,因为我希望得到更详细的答案,需要详细说明:

  1. 关于print("its an error") return Never之间差异的实际示例作为返回类型

  2. 我们应该采用这些返回类型的条件。

  3. 也有可能返回类型为零;我也需要对该功能进行比较

  4. 答案应该集中在差异上。

4 个答案:

答案 0 :(得分:23)

Never 在Swift 3中引入了返回类型,以替换 @noreturn 键。

见本提案中的理由:
 SE-0102 Remove @noreturn attribute and introduce an empty Never type

正如官方文件所述:

  

不能正常返回的函数的返回类型;一个类型   没有价值观。

     

在声明闭包时使用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!")
}

Erica Sadun引用的@noreturn的使用细节和优势:

  • 绝不允许函数或方法抛出:例如()抛出 - >决不。抛出允许辅助路径进行错误修复,即使在预期不会返回的函数中也是如此。
  • 作为第一类类型,永远不会以@noreturn属性不能的方式使用泛型。
  • 切勿主动阻止某个功能同时声明返回类型和不返回。这是旧系统下的一个潜在问题。

首先注意(关于二级错误修复)可能特别重要。 Never函数可以有复杂的逻辑和抛出 - 不一定会崩溃。

让我们看一些有趣的用例,并在NeverVoid之间进行比较

从不

示例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 告诉编译器没有返回来电网站。应用程序runloop终止。

答案 1 :(得分:6)

<强> 空隙

Void本身就是一个返回类型,它是一个零元素的元组。您可以互换使用Void和()。

看看这些例子,

  1. func yourFunc() {} 这是一个没有返回类型的函数,它基本上返回一个零元素的元组,可以写成()

  2. func yourFunc() -> Void {} 明确通知编译器返回类型为void的函数

  3. func yourFunc() -> () {} 此返回类型()显示与void类型相同。 ()表示元素为零的元组

  4. <强>从不

    永远不会返回类型通知编译器不需要返回空元组()。此外,具有永不返回类型的函数用于当前执行的退出点,如崩溃,致命错误,中止或退出。

    要详细了解从不,让我们看一下abort()示例:

    1

     func yourFunc() {
        abort()
        print("Will not reach at this point") //Warning for this line
    } 
    

    2

     func yourFunc() -> Int {
        if true {
            abort()
        } else {
            return 1
        }
    }
    

    从上面的代码片段中,我们可以看到当我们调用abort()(它不返回值)作为函数中的最后一个语句,该函数期望返回一个值(在我们的例子中是Int) 。编译器不会生成警告。

    <强>中止()

    public func abort() -> Never
    

    同样适用于退出():

    public func exit(_: Int32) -> Never
    

    Apple文档说:“在声明无条件抛出错误,陷阱或以其他方式终止的闭包,函数或方法时,使用Never作为返回类型。

    因此,如果您想编写一个记录灾难性错误的自定义函数,您应该使用返回类型Never来向编译器发出信号:

    func catastrophicErrorDisplay(error: String) -> Never {
        DisplaySomeCustomLogFacility(error)
    }
    

    简而言之,“永远不会用于突然和完全失败,无法恢复。

答案 2 :(得分:4)

Never表示该函数永远不会返回。它旨在用于fatalError等导致程序故意崩溃的事情,通常是在记录错误之后。你可能不应该使用它,除非你正在为应用程序中的灾难性错误做一个处理程序。

这与只是没有返回值的函数不同,就像在第二个代码段中一样。您也可以将其写为func addNums() -> Void

答案 3 :(得分:2)

为了更好地理解NeverVoid,以及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()。您还可以使用&#34;值&#34;像这样回来了:

func empty() -> Void {}
let v = empty()
print(type(of: v)) // prints "()"

虽然编译器会警告&#34; Constant&#39; v&#39;推断出类型为'Void&#39;,这可能是出乎意料的&#34;。

根据类型系统而不是特殊语言功能定义NeverVoid使我们能够使用泛型做一些非常聪明的事情。让我们看一下Result类型的示例,对成功和失败类型都是通用的。

enum Result<R, E> {
    case success(R)
    case failure(E)
}

可能的专长是Result<Void, MyError>。这意味着你的结果是,在成功的情况下,除了成功之外没有任何信息。

另一种可能性是Result<String, Never>。编译器保证这个结果永远不会失败。

Optionals以类似的方式与NeverVoid互动。 Never?只能是零,而Void?只保留信息,无论是否为零,仅此而已(它基本上是一个更复杂的Bool)。这些都不是非常有用,但可能会在NeverVoid用作某处的通用参数时出现。

在实践中,您很少会编写返回Never的函数。我个人用它来包装fatalError来创建一个我用来标记尚未实现的函数的函数:

func unimplemented(f: String = #function) -> Never {
    fatalError("\(f) is not implemented yet")
}

返回Never的函数的另一个示例是dispatchMain(),可以在命令行实用程序中使用它来启动DispatchQueue.main。由于此队列等待新块,dispatchMain()永远不会返回。