Search code examples
swiftnoreturn

When and how to use @noreturn attribute in Swift?


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.


Solution

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