When and how to use @noreturn attribute in Swift?

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

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

Tags:

Swift

Noreturn