Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic Swift 4 enum with Void associated type

tl;dr

Is it possible to instantiate a generic Swift 4 enum member with an associated value of type Void?

Background

I'm using a simple Result enum (similar to antitypical Result):

enum Result<T> {
  case success(T)
  case error(Error?)
}

Now I'd like to use this enum to represent the result of an operation which does not yield an actual result value; the operation is either succeeded or failed. For this I'd define the type as Result<Void>, but I'm struggling with how to create the Result instance, neither let res: Result<Void> = .success nor let res: Result<Void> = .success() works.

like image 296
dr_barto Avatar asked Aug 23 '17 11:08

dr_barto


2 Answers

In Swift 3 you can omit the associated value of type Void:

let res: Result<Void> = .success()

In Swift 4 you have to pass an associated value of type Void:

let res: Result<Void> = .success(())
// Or just:
let res = Result.success(())
like image 142
Martin R Avatar answered Nov 10 '22 02:11

Martin R


In Swift 4, an enum case with a Void associated value is no longer equivalent to a an enum case with an empty list of associated values.

I believe this is, as Martin says, a result of SE-0029 where you can no longer pass a tuple of arguments to a function and have them "splat" across the parameters (although the proposal was marked implemented in Swift 3, I believe this particular case was picked up later in the implementation of SE-0110 for Swift 4).

As a result, this means you can no longer call a (Void) -> T as a () -> T in Swift 4. You now have to pass Void in explicitly:

let result = Result.success(())

However, I find this pretty ugly, so I usually implement an extension like this:

extension Result where Success == Void {
    static var success: Result {
        return .success(())
    }
}

Which lets you say things like this:

var result = Result.success
result = .success

It's worth noting that this workaround isn't just limited to enum cases, it can be also used with methods in general. For example:

struct Foo<T> {
  func bar(_ a: T) {}
}

extension Foo where T == Void {
  func bar() { bar(()) }
}

let f = Foo<Void>()

// without extension:
f.bar(())

// with extension:
f.bar()
like image 52
Hamish Avatar answered Nov 10 '22 03:11

Hamish