【问题标题】:What is the Never return type?什么是 Never 返回类型?
【发布时间】:2018-04-04 18:56:18
【问题描述】:

返回类型为Neverfunc 有什么作用?

例如:

func addNums() -> Never {

    //my code

}

如果我将返回类型保持为Void 这样会有什么不同?

func addNums() -> Void {

    //my code

}

假设我希望处理fatalError(如dpassage 所说);下面的代码就足够了:

print("its an error")
return

Apple 文档说:

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

来源:Developer

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

  1. 关于NeverVoid作为返回类型的区别的实例

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

  3. 返回类型也有可能为 nil;我也需要比较该功能

答案应该集中在差异上。

【问题讨论】:

标签: swift types


【解决方案1】:

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

这与不返回值的函数不同,如第二个 sn-p。你也可以写成func addNums() -> Void

【讨论】:

    【解决方案2】:

    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!")
    }
    

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

    • 从不允许函数或方法抛出:例如() 抛出 -> 从不。抛出允许错误补救的辅助路径,即使在不期望返回的函数中也是如此。
    • 作为第一类类型,从不以@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 告诉编译器没有返回调用者站点。应用程序运行循环已终止。

    【讨论】:

      【解决方案3】:

      为了更好地理解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()。您也可以使用返回的“值”,如下所示:

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

      尽管编译器会警告“Constant 'v' inferred to have type 'Void', which may be unexpected”。


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

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

      可能的特化是Result&lt;Void, MyError&gt;。这意味着您有一个结果,在成功时,除了它成功的事实之外不包含任何信息。

      另一种可能是Result&lt;String, Never&gt;。编译器保证这个结果永远不会失败。

      Optional 以类似的方式与 NeverVoid 交互。 Never? 只能是 nil,Void? 只保存它是否为 nil 的信息,仅此而已(它基本上是一个更复杂的 Bool)。这两者本身并不是很有用,但当NeverVoid 在某处用作泛型参数时可能会出现。


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

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

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

      【讨论】:

        【解决方案4】:

        无效

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

        看看这些例子,

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

        2. func yourFunc() -&gt; Void {} 显式通知编译器返回类型为 void 的函数

        3. func yourFunc() -&gt; () {}这个()的返回类型显示和void类型一样。 () 表示零元素的元组

        从不

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

        为了详细了解never,我们来看一个 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
            }
        }
        

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

        中止()

        public func abort() -> Never
        

        exit() 类似:

        public func exit(_: Int32) -> Never
        

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

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

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

        简而言之,“从不用于无法恢复的突然且完全的故障。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-03-20
          • 2015-12-08
          • 2018-01-17
          • 1970-01-01
          • 2023-03-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多