When and how to use @noreturn attribute in Swift?

蹲街弑〆低调 提交于 2019-12-28 13:51:32

问题


I read the code block enclosed in curly braces after the keyword else in the context of a guard-else flow, must call a function marked with the noreturn attribute or transfer control using return, break, continue or throw.

The last part is quite clear, while I don't understand well the first.

First of all, any function returns something (an empty tuple at least) even if you don't declare any return type. Secondly, when can we use a noreturn function? Are the docs suggesting some core, built-in methods are marked with noreturn?

The else clause of a guard statement is required, and must either call a function marked with the noreturn attribute or transfer program control outside the guard statement’s enclosing scope using one of the following statements:

return

break

continue

throw

Here is the source.


回答1:


First of all, any function returns something (an empty tuple at least) even if you don't declare any return type.

(@noreturn is obsolete; see Swift 3 Update below.) No, there are functions which terminate the process immediately and do not return to the caller. These are marked in Swift with @noreturn, such as

@noreturn public func fatalError(@autoclosure message: () -> String = default, file: StaticString = #file, line: UInt = #line)
@noreturn public func preconditionFailure(@autoclosure message: () -> String = default, file: StaticString = #file, line: UInt = #line)
@noreturn public func abort()
@noreturn public func exit(_: Int32)

and there may be more.

(Remark: Similar annotations exist in other programming languages or compilers, such as [[noreturn]] in C++11, __attribute__((noreturn)) as a GCC extension, or _Noreturn for the Clang compiler.)

You can mark your own function with @noreturn if it also terminates the process unconditionally, e.g. by calling one of the built-in functions, such as

@noreturn func myFatalError() {
    // Do something else and then ...
    fatalError("Something went wrong!")
}

Now you can use your function in the else clause of a guard statement:

guard let n = Int("1234") else { myFatalError() }

@noreturn functions can also be used to mark cases that "should not occur" and indicate a programming error. A simple example (an extract from Missing return UITableViewCell):

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell: MyTableViewCell

    switch (indexPath.row) {
    case 0:
        cell = tableView.dequeueReusableCellWithIdentifier("cell0", forIndexPath: indexPath) as! MyTableViewCell
        cell.backgroundColor = UIColor.greenColor()
    case 1:
        cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! MyTableViewCell
        cell.backgroundColor = UIColor.redColor()
    default:
        myFatalError()
    }
    // Setup other cell properties ...
    return cell
}

Without myFatalError() marked as @noreturn, the compiler would complain about a missing return in the default case.


Update: In Swift 3 (Xcode 8 beta 6) the @noreturn attribute has been replaced by a Never return type, so the above example would now be written as

func myFatalError() -> Never  {
    // Do something else and then ...
    fatalError("Something went wrong!")
}



回答2:


simple playground to see how it works ...

//: Playground - noun: a place where people can play

import Foundation
@noreturn func foo() {
    print("foo")
    exit(1)
}

var i: Int?

guard let i = i else {
    foo()
}

print("after foo") // this line will never executed

//prints foo and finish



回答3:


For instance, consider an assync operation that return either a value or error in result type. We usually write this as follows.

enum Result<Value, Error> {
    case success(Value)
    case failure(Error)
}

func fetch(_ request: URLRequest,
          completion: (Result<(URLResponse, Data), Error>) -> Void) {
    // ...
}

Now, if you know a function that always return value, never an error, then we could write :

func alwaysSucceeds(_ completion: (Result<String, Never>) -> Void) {
    completion(.success("yes!"))
}

When compiler see Never, it won't force you to write all the switch cases to be exhaustive, so you can skip .failure as follows.

alwaysSucceeds { (result) in
    switch result {
    case .success(let string):
        print(string)
    }
}

Ref : https://nshipster.com/never/



来源:https://stackoverflow.com/questions/38098608/when-and-how-to-use-noreturn-attribute-in-swift

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!