kelan.io

Voids

I recently ran into a problem where I was confused by the difference between () and Void in Swift.

TL;DR Void is a type, but not a value, and () is both a type and a value.

Sometimes they’re the same

My confusion arose from the fact that I think of them as interchangeable, when it comes to function signatures. For example, these two are the same:

func asyncWork1(completion: @escaping (String) -> Void)
func asyncWork2(completion: @escaping (String) -> ())

But not always

However, given this generic enum definition1:

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

I was trying to represent the success case, but without a value. So I tried this:

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

But that gave this (somewhat cryptic) error:

error: member 'success' in 'Result<Void>' (aka 'Result<()>') produces result of type 'Result<T>', but context expects 'Result<Void>' (aka 'Result<()>')

It’s because Void is just a type, not a value, and the compiler is expecting a value here.

So, to fix it, we use () instead, which is a value.

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

And, since we’ve realized that the issue is Void is a type, not a value, it seems obvious to try this, which also works (but does feel a bit odd to me 2):

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

Parting Thoughts

I do wonder a bit about why () is allowed as a type as well as a value 3. I think it has to do with the subtle details of Swift’s syntax, and tuples, and the desire to be able to write () -> () as a function signature, instead of Void -> Void? But, it did lead to some confusion in this case (no pun intended).


Thanks, as usual, to Jacob for pointing out the type/value difference to me here.


  1. The fact that it’s a Result isn’t important, but just is the real example of where this came up for me. 

  2. And not just because it’s literally just adding 4 extra, unnecessary characters. 

  3. The Void() solution briefly made me think it would be funny to try to explain this by saying “the empty parens () are used for calling the constructor of the type next to it, and since there is nothing next to it, it constructs a Void”. But that’s almost too plausible. 🙃