It’s Now or Never…

I totally forgot I promised to post about Never. So first some apology Elvis. And that done, let me introduce you to this new Swift 3 keyword.

SE-0102 removes the rarely-used @noreturn function type and replaced it with a dead-end return type:

/// The type of expressions that can never happen.
public /*closed*/ enum Never { /*no values*/ }

func foo() -> Never {
  fatalError("no way out!")
}

Using Never allows you to express the condition where a method or function, whether by dispatch (completion handlers, for example, although you generally don’t need Never for that use-case) or expected exit (for example, a usage() function in a command-line Swift app) will never return control back to the call site.

I haven’t used Never very much. To date, it’s mostly a command line thing for me, and one where I’m more testing the waters than using confident assurance.

// Fetch arguments and test for usage
var arguments = CommandLine.arguments
let appName = arguments.remove(at: 0).lastPathComponent
func usage() -> Never { print("Usage: \(appName) location1 location2"); exit(-1) }
guard arguments.count == 2 else { usage() }

The Swift compiler picks up the Never return type in the guard statement, accepting that the else clause leaves scope.

According to Joe Groff’s proposal, there are several advantages of using Never over @noreturn.

  • Never allows a function or method to throw: e.g. () throws -> Never. Throwing allows a secondary path for error remediation, even in functions that were not expected to return.
  • As a first class type, Never works with generics in a  way that the @noreturn attribute could not.
  • Never proactively prevents a function from claiming both a return type and no-return at the same time. This was a potential issue under the old system.

Joe writes in summary: “The net result is a simpler, more consistent, and more expressive model for handling nonreturning functions.”

3 Comments

  • I tried using Never for my API module callbacks but couldn’t really figure out how to use it when my callbacks contains guards.

    Example

    Do you have any hints on how to properly use Never for such case.

    • It makes more sense when the completion handler will end the app, no? Quick answer (really deserves its own post):

      typealias CompletionNever = () -> Never
      
      func functionWithNeverCompletion(completion: CompletionNever) {
          completion()
      }
      
      
      functionWithNeverCompletion {
          alldone: do {
              guard somecondition else { break alldone }
              print("Hello")
          }
      }
      • Yeah so to me it doesn’t seem suitable for completion blocks in my case.

        Sad, I wished it was what I was looking for. I’ll stick with my returns in guards for now I guess ????