Never

“Never” is a proposition that an event doesn’t occur at any time in the past or future. It’s logical impossibility with a time axis; nothingness stretching out in all directions, forever.

…which is why it’s especially worrisome to encounter this comment in code:

// this will never happen

Every compiler textbook will tell you that a comment like this one can’t and won’t affect the behavior of compiled code. Murphy’s Law says otherwise.

How does Swift keep us safe in the unpredictable chaos that is programming? You’ll never believe the answer: nothing and crashing.


Never was proposed as a replacement for the @noreturn attribute in SE-0102: “Remove @noreturn attribute and introduce an empty Never type”, by Joe Groff.

Prior to Swift 3, functions that stop execution, like fatalError(_:file:line:), abort(), and exit(_:), were annotated with the @noreturn attribute, which told the compiler that there was no return to the caller site.

// Swift < 3.0
@noreturn func fatalError(_ message: () -> String = String(),
                               file: StaticString = #file,
                               line: UInt = #line)

After the change, fatalError and its trapping cohorts were declared to return the Never type:

// Swift >= 3.0
func fatalError(_ message: @autoclosure () -> String = String(),
                     file: StaticString = #file,
                     line: UInt = #line) -> Never

For a type to replace the functionality of an annotation, it must be pretty complex, right? Nope! Actually, just the opposite — Never is arguably the simplest type in the entire Swift standard library:

enum Never {}

Uninhabited Types

Never is an uninhabited type, which means that it has no values. Or to put it another way, uninhabited types can’t be constructed.

Enumerations with no cases are the most common example of an uninhabited type in Swift. Unlike structures or classes, enumerations don’t receive an initializer. And unlike protocols, enumerations are concrete types that can have properties, methods, generic constraints, and nested types. Because of this, uninhabited enumeration types are used throughout the Swift standard library to do things like namespace functionality and reason about types.

But Never isn’t like that. It doesn’t have any fancy bells or whistles. It’s special by virtue of it being what it is (or rather, isn’t).

Consider a function declared to return an uninhabited type: Because uninhabited types don’t have any values, the function can’t return normally. (Because how could they?) Instead, the function must either stop execution or run indefinitely.

Eliminating Impossible States in Generic Types

Sure, this is interesting from a theoretical perspective, but what practical use does Never have for us?

Not much — or at least not before the acceptance SE-0215: Conform Never to Equatable and Hashable.

In his proposal, Matt Diephouse explains the motivation behind conforming this obscure type to Equatable and other protocols this way:

Never is very useful for representing impossible code. Most people are familiar with it as the return type of functions like fatalError, but Never is also useful when working with generic classes. For example, a Result type might use Never for its Value to represent something that always errors or use Never for its Error to represent something that never errors.

Swift doesn’t have a standard Result type, but most of them look something like this:

enum Result<Value, Error> {
    case success(Value)
    case failure(Error)
}

Result types are used to encapsulate values and errors produced by functions that execute asynchronously (whereas synchronous functions can use throws to communicate errors).

For example, a function that makes an asynchronous HTTP request might use a Result type to wrap either a response and data or an error:

func fetch(_ request: URLRequest,
          completion: (Result<(URLResponse, Data), Error>) -> Void) {
    // ...
}

When calling that method, you’d switch over result to handle .success and .failure separately:

fetch(request) { result in
    switch result {
    case let .success(response, _):
        print("Success: \(response)")
    case .failure(let error):
        print("Failure: \(error)")
    }
}

Now consider a function that’s guaranteed to always return a successful result in its completion handler:

func alwaysSucceeds(_ completion: (Result<String, Never>) -> Void) {
    completion(.success("yes!"))
}

By specifying Never as the result’s Error type, we’re using the type system to signal that failure is not an option. What’s really cool about this is that Swift is smart enough to know that you don’t need to handle .failure for the switch statement to be exhaustive:

alwaysSucceeds { (result) in
    switch result {
    case .success(let string):
        print(string)
    }
}

You can see this effect played out to its logical extreme in the implementation conforming Never to Comparable:

extension Never: Comparable {
  public static func < (lhs: Never, rhs: Never) -> Bool {
    switch (lhs, rhs) {}
  }
}

Because Never is an uninhabited type, there aren’t any possible values of it. So when we switch over lhs and rhs, Swift understands that there aren’t any missing cases. And since all cases —- of which there aren’t any — return Bool, the method compiles without a problem.

Neat!


Never as a Bottom Type

As a corollary, the original Swift Evolution proposal for Never hints at the theoretical usefulness of the type with further enhancement:

An uninhabited type can be seen as a subtype of any other type — if evaluating an expression never produces a value, it doesn’t matter what the type of that expression is. If this were supported by the compiler, it would enable some potentially useful things…

Unwrap or Die

The forced unwrap operator (!) is one of the most controversial parts of Swift. At best, it’s a necessary evil. At worst, it’s a code smell that suggests sloppiness. And without additional information, it can be really tough to tell the difference between the two.

For example, consider the following code that assumes array to not be empty:

let array: [Int]
let firstIem = array.first!

To avoid force-unwrapping, you could use a guard statement with conditional assignment instead:

let array: [Int]
guard let firstItem = array.first else {
    fatalError("array cannot be empty")
}

In the future, if Never is implemented as a bottom type, it could be used in the right-hand side of nil-coalescing operator expression.

// Future Swift? 🔮
let firstItem = array.first ?? fatalError("array cannot be empty")

If you’re really motivated to adopt this pattern today, you can manually overload the ?? operator thusly (however…):

func ?? <T>(lhs: T?, rhs: @autoclosure () -> Never) -> T {
    switch lhs {
    case let value?:
        return value
    case nil:
        rhs()
    }
}

Expressive Throw

Similarly, if throw is changed from being a statement to an expression that returns Never, you could use throw on the right-hand side of ??:

// Future Swift? 🔮
let firstItem = array.first ?? throw Error.empty

Typed Throws

Looking even further down the road: If the throws keyword in function declarations added support for type constraints, then the Never type could be used to indicate that a function won’t throw (similar to the Result example before):

// Future Swift? 🔮
func neverThrows() throws<Never> {
    // ...
}

neverThrows() // `try` unnecessary because it's guaranteed to succeed (perhaps)

Making a claim that something will never be the case can feel like an invitation for the universe to prove otherwise. Whereas modal or doxastic logics allow for face-saving compromise (“it was true at the time, or so I believed!”), temporal logic seems to hold propositions to a higher standard.

Fortunately for us, Swift lives up to this higher standard thanks to the unlikeliest of types, Never.

NSMutableHipster

Questions? Corrections? Issues and pull requests are always welcome — NSHipster is made better by readers like you.

This article uses Swift version 4.2. Find status information for all articles on the status page.

Next Article

Machine learning has been at the heart of natural language processing in Apple platforms for many years, but it’s only recently that external developers have been able to harness it directly.