When and How to Use @Noreturn Attribute in Swift

When and how to use @noreturn attribute in Swift?

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

Is there any difference between never and void return type in swift? what is the use of never return type in practically?

Void

A return type that doesn’t return a value.

Never

Something that will never return.

Void vs Never

  • Implementation: Void is a tuple, Never is an empty enum.
  • Never informs the compiler that there is no need to return a value.
  • Never is used in methods that will unconditionally throw an error or crash the system.

Convincing Swift that a function will never return, due to a thrown Exception

Swift's @noreturn attribute marks functions and methods as not returning to their caller.

As probably the simplest example, the signature of the built-in function abort()'s is:

@noreturn func abort()

This gives the compiler all the information it needs. For example, the following will compile just fine:

func alwaysFail() -> Int {
abort()
}

Although alwaysFail() theoretically returns an Int, Swift knows that execution can't continue after abort() is called.

The reason my original code didn't work is because NSException.raise is a pre-Swift method, and therefore doesn't have the @noreturn attribute. To easily solve this, I can either use abort():

func shouldBeOverridden() -> ReturnType {
println("Subclass has not implemented abstract method `shouldBeOverridden`!")
abort()
}

or, if I still want to use NSException, I can define an extension with the proper attribute

extension NSException {
@noreturn func noReturnRaise() {
self.raise()
abort() // This will never run, but Swift will complain that the function isn't really @noreturn if I don't call a @noreturn function before execution finishes.
}
}

As a third option, I can just use a never-called abort() after an NSException.raise() to placate the compiler. The earlier option, using an extension, is really just an abstraction for doing this:

func shouldBeOverridden() -> ReturnType {
let exception = NSException(
name: "Not implemented!",
reason: "A concrete subclass did not provide its own implementation of shouldBeOverridden()",
userInfo: nil
)
exception.raise()
abort() // never called
}


Related Topics



Leave a reply



Submit