Ian Bell Ian Bell - 5 months ago 64
Swift Question

When and how to use @noreturn attribute in Swift

I red 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



(source: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html)

Answer

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

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.