Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Because Swift does not have abstract methods, I am creating a method whose default implementation unconditionally raises an error. This forces any subclass to override the abstract method. My code looks like this:

class SuperClass {
    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()
    }
}

The problem: Because the function is expected to return a value, and the above function has no return statement, compilation fails. I need some way to convince the compiler that this function could never complete execution, because it will always raise an error.

How could this be done in Swift, when all of the error-handling seems be library-level, and therefore beyond the understanding of the compiler? Is there any kind of language-level feature for (hopefully gracefully) terminating the execution of a program?

like image 203
exists-forall Avatar asked Jan 07 '15 21:01

exists-forall


People also ask

What is never return type in Swift?

The Never return type is a special one in Swift, and tells the compiler that execution will never return when this function is called. It's used by Swift's fatalError() and preconditionFailure() functions, both of which cause your app to crash if they are called.

How would you call a function that throws errors and also returns a value?

It's called rethrows . The rethrows keyword is used for functions that don't directly throw an error.

Does Return exit the function Swift?

After a return statement, Swift will execute a function that returns Void before exiting that function. In the above example, since the print function returns Void it will be executed before exiting myFunction.


2 Answers

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
}
like image 96
exists-forall Avatar answered Nov 01 '22 20:11

exists-forall


In Xcode 8 beta 6 (Swift 3 beta 6) you can now use the Never return type instead of @noreturn to indicate that a function won't return to its caller:

func crash() -> Never {
    fatalError("Oops")
}
like image 21
Luke Avatar answered Nov 01 '22 20:11

Luke