Dec 12, 2016

Why You Can't Assign nil to Just Anything in Swift

Swift takes a strong stance on which types can be assigned nil. For example, this is an error:

var i = 1
i = nil //error: nil cannot be assigned to type 'Int'
// It is also an error for objects:
var obj = NSObject()
obj = nil //error: nil cannot be assigned to type 'NSObject'

Many languages struggle to express the notion of absence of value. The problem is that it is often implemented in a way that makes it indistinguishable from the integer 0. It is also often easy to mistake for a pointer. Objective-C suffered from this, and there were several ways to indictate an item has no value: nil, NULL, NSNull. For scalar values, something like NSIntegerMax was often used.

Swift has a single way to express absence of value for all types. Conversely, nil indicates the absence of a value and nothing else. The Optional type is the canonical way to express this absence. It is impossible to mistake a nil object with the number zero, or for a pointer to memory. You will never question whether a value is nil or really 0.

This strict separation between value and absense-of-value means you can't assign nil just to anything. The literal nil is assignable only to types which implement the protocol ExpressibleByNilLiteral. And there is exactly one type in the standard library that implements that protocol: the Optional enumeration. In other words, you can assign the value nil only to Optionals.1 Because nil is not a number, you can't assign it to an Int.

An Optional is generic, so you can have Optional<Int>, an Optional<Double>, an Optional<NSObject> and so on. The Optional is the type that can take a nil assignment. An Optional<Int> either has no value, or it has an intenger value, possibly 0.

If you have an instance that might lack any meaningful value in its lifetime, declare it as an optional. When it has no meaningful value, assign it nil.

var i: Int? = 1 // same as var i: Optional<Int> = 1
var j: Int? // uninitialized optionals are implicitly nil
i = nil // Optional accepts nil assignment
var obj: NSObject? = NSObject()
obj = nil // Optional accepts nil assignment

Also note that these are three very diffent values:

let zeroValue: Int = 0
let nilOptional: Int? = nil
let zeroOptional: Int? = 0

zeroValue == 0      // true
zeroValue == nil    // false
nilOptional == 0    // false
nilOptional == nil  // true
zeroOptional == 0   // true
zeroOptional == nil // false

If you ever get a compiler error about assigning nil to a non-optional, this is why. The benefit is that if you ever do encounter a nil optional, you won't have to second-guess whether the value is meant to indicate absence of value. This is true whether it it an object you created, or whether you received it from another library.

  1. Implementing ExpressibleByNilLiteral on any other type is explicitly discouraged by Apple, and would break the semantic meaning of nil. Don't do that.