Swift Optional Type: How .None == Nil Works

Swift Optional type: how .None == nil works

enum Optional conforms to the NilLiteralConvertible protocol,
which means that it can be initialized with the "nil" literal.
The result is Optional<T>.None where the type placeholder T
must be inferred from the context.

As an example,

let n = nil // type of expression is ambiguous without more context

does not compile, but

let n : Int? = nil

does, and the result is Optional<Int>.None.

Now optionals can in general not be compared if the underlying
type is not Equatable:

struct ABC { }

let a1 : ABC? = ABC()
let a2 : ABC? = ABC()

if a1 == a2 { } // binary operator '==' cannot be applied to two 'ABC?' operands

and even this does not compile:

if a1 == Optional<ABC>.None { } // binary operator '==' cannot be applied to two 'ABC?' operands

But this compiles:

if a1 == nil { } 

It uses the operator

public func ==<T>(lhs: T?, rhs: _OptionalNilComparisonType) -> Bool

where _OptionalNilComparisonType is not documented officially.
In https://github.com/andelf/Defines-Swift/blob/master/Swift.swift the
definition can be found as (found by @rintaro and @Arsen, see comments):

struct _OptionalNilComparisonType : NilLiteralConvertible {
init(nilLiteral: ())
}

This allows the comparison of any optional type with "nil", regardless of whether the underlying type is Equatable or not.

In short – in the context of Optionalnil can be thought of as a shortcut to .None, but the concrete type must be inferred from the context. There is a dedicated == operator for comparison with "nil".

Why non optional Any can hold nil?

TL;DR; Optionals in swift are translated by the compiler to Optional enum instances, and since Any can map to any value, it can be used to store optionals.


How does Swift represent optionals? It does it by mapping SomeType? to a concrete implementation of the Optional enum:

Int? => Optional<Int>
String? => Optional<String>

A simplified declaration of Optional looks like this:

enum Optional<T> {
case none // nil
case some(T) // non-nil
}

Now, a variable of type Any is able to hold an enum value (or any other kind of value, or even metatype information), so it should be able to hold for example a nil String, aka String?.none, aka Optional<String>.none.

Let's see what happens, though. As we see by the Optional declaration, nil corresponds to the .none enum case for all types:

nil == Optional<String>.none // true
nil == Optional<Int>.none // true
[Double]?.none == nil // also true

So theoretically, you should be able to assign nil to a variable declared as Any. Still, the compiler doesn't allow this.

But why doesn't the compiler let you assign nil to an Any variable? It's because it can't infer to which type to map the .none enum case. Optional is a generic enum, thus it needs something to fill the T generic parameter, and plain nil is too broad. Which .none value should it use? The one from Int, the one from String, another one?

This gives an error message supporting the above paragraph:

let nilAny: Any = nil // error: nil cannot initialize specified type 'Any' (aka 'protocol<>')

The following code works, and is equivalent to assigning a nil:

let nilAny: Any = Optional<Int>.none

, as the above Any variable is actually holding a valid value of the Optional enum.

Indirect assignments work too, as behind the scenes nil is converted to Optional<Type>.none.

var nilableBool: Bool? // nilableBool has the Optional<Bool>.none value
var nilBoolAsAny: Any = nilableBool // the compiler has all the needed type information from nilableBool

Unlike other languages, in Swift nil corresponds to a concrete value. But it needs a type to work with, for the compiler to know which Optional<T>.none it should allocate. We can think of the keyword as providing sugar syntax.

Are nil and Optional T .None the same thing in Swift?

If you don’t provide an initial value when you declare an optional
variable or property, its value automatically defaults to nil.

They have same value, both are nil.

Actually Optional<T>.None is a polymorphic primitive value, and nil is a constant having this value. Optional<T> is a polymorphic type. The type of nil is Optional<T>. That's why you cannot assign nil to anything else but an Optional<T>. For the same reason you cannot assign true to anything but a Bool.

 

For now, according to the documentation you can not use nil for any custom and arbitrary type but optionals.

nil cannot be used with non-optional constants and variables. If a
constant or variable in your code needs to be able to cope with the
absence of a value under certain conditions, always declare it as an
optional value of the appropriate type.

Confused about optional type in swift ( Nil can compare to Int )

(Edit update: after additional question added by OP)

I'll add an answer to you question

"Does it make sense "The nil can compare to Int" in Swift?"

You can consider Optional a type just like Int or double, Double, with a (simplified) enum implementation

enum Optional<T> {
case None
case Some(T)

init(_ value: T) {
self = .Some(value)
}

init() {
self = .None
}
}

The generic T in the Optional type is never used for case .None, which, in this discussion, would be nil. Hence, any type that is also optional (e.g. Int?, String? and so on) can be compared to nil. For the sake of this discussion, you could almost think of nil as a single literal that can be used to initialize or give value to any type that is defined as optional. With this latter statement, it's obvious that we can also compare the values of our optional type to nil.

Hence: yes, we can compare the value of any optional variable to nil, but there are usually better options, e.g. optional chaining or the nil coalescing operator (??).


(From before edit, still relevant)

I'll add an example to help to show you what is going on in loops constructed as if a.b?.c != d { ....

Consider the following structures:

struct MyStruct {
var myRow : Int
init (row: Int) {
myRow = row
}
}

struct MyTopStruct {
var myStruct : MyStruct? = nil

mutating func setValueToMyStruct(row: Int) {
myStruct = MyStruct(row: row)
}
}

Let a be an instance of MyTopStruct, and lets have some integer and optional integer properties in our example:

var a = MyTopStruct()
let myInt = 10
var myOptionalInt : Int? = nil

Then, we have a few different outcomes for if a.b?.c != d { ... clauses, depending on whether optionals in this example are nil (as initialized) or not.

/* "a.myStruct?" returns nil, and this clause will hence
compare "nil != myInt"?, which will always be true, since
myInt is a non-optional integer */
if a.myStruct?.myRow != myInt {
print("Entered 1") // prints
}

/* "a.myStruct?" still nil, and myOptionalInt not yet initialised
so also nil. Hence, "nil != nil"? => false */
if a.myStruct?.myRow != myOptionalInt {
print("Entered 2") // doesnt print
}

/* now initialise a.myStruct */
a.setValueToMyStruct(myInt)

/* "a.myStruct?" now returs value of myInt (10), which != nil,
the latter being the current value of myOptionalInt
=> always enters */
if a.myStruct?.myRow != myOptionalInt {
print("Entered 3") // prints
}

/* finally initialize myOptionalInt */
myOptionalInt = 9

/* Now this is the first comparison that can actually behave
in "different ways" w.r.t. how we usually think of if clauses:
comparing _values_ of same-type objects.
Hence, comparison now depends on the two non-nil values of
a.myStruct?.myRow and myOptionalInt */
if a.myStruct?.myRow != myOptionalInt {
print("Entered 4") // prints, since 10 != 9
}

Swift nil Any != nil

Think of Any and Optional<Wrapped> like boxes.

  1. Any is an opaque box that can hold anything. You can gleam what's inside by trying to cast it using as?.
  2. Optional<Wrapped> is a transparent box. It lets you see that its contents are either a value of Optional<Wrapped>.some(wrapped) or Optional<Wrapped>.none().

An Optional<Any> and Any containing an Optional<Wrapped> aren't the same thing.

1. x

is a value of Optional<Any>.none, a.k.a. nil. nil gets printed, so that makes sense.

2. type(of: x)

is the type of x, Optional<Any>, as we expected.

3. x == nil

is calling an == operator of type (T, T) -> Bool. Since both args need to be the same type, and since x has a value of Optional<Any>.none (of type Optional<Any>), nil is inferred to be Optional<Any>.none. The two are equivalent, so we get true, as expected.

4. y

is a value of type Any. At compile time, nothing more is known about y. It just so happens that the value behind hidden by the opaque curtain of Any is x. print is implemented to "see through" Any instances, to show you what's really there.

5. type(of: y)

is doing something similar to print. It's using runtime type information to see exactly what the runtime value stored into y is. The static type of y is Any, but type(of: y) shows us that the runtime type of its value is Optional<Any>.

6. y == nil

This is where it gets a little trickier.

The choice of which overload of a function or operator to call is done at compile time, based on the statically known types of the operands. That is to say, operators and functions aren't dynamically dispatched based on the types of their arguments, the way they're dynamically dispatched based on the type of their objects (e.g. the foo in foo.bar()).

The overload that's selected here is ==(lhs: Wrapped?, rhs: _OptionalNilComparisonType) -> Bool. You can see its implementation on lines 449-481 of Optional.swift.

Its left-hand-side operand is Wrapped?, a.k.a. an Optional<Wrapped>.

Its right-hand-side operand is _OptionalNilComparisonType. This is a special type that conforms to ExpressibleByNilLiteral.

Optional is one type that conforms to ExpressibleByNilLiteral, meaning that you can write let x: Optional<Int> = nil, and the compiler could use that protocol to understand that nil should mean Optional<Int>.none. The ExpressibleByNilLiteral protocol exists to allow this behaviour to be extensible by other types. For example, you can imagine a Python interopability framework, where you would want to be able to say let none: PyObject = nil, which could be passed into Python and resolve to None, Python's null type.

What's happening here is that the left hand side (y, of type Any), is being promoted to type Any?. The promoted value has type Optional<Any>.some(old_value_of_y). The right hand side, the nil, is being used to instantiate a value of _OptionalNilComparisonType, equivalent to calling _OptionalNilComparisonType.init(nilLiteral: ()).

Thus, your call site is equivalent to:

Optional<Any>.some((Optional<Any>.none) as Any) == _OptionalNilComparisonType(nilLiteral: ()) /*
↑ ↑↖︎_the value of x_↗︎ ↑↑
↑ ↖︎_the value of y _________↗︎↑
↖︎_the value of y after promotion to optional__↗︎ */

According to the implementation, the left side is a some, the right side is a nil, and thus they're unequal.

You might say:

Well this is pretty non-obvious behaviour, it's not what I wanted.

To which I would respond:

Don't ignore the compiler errors, they're right on point:

warning: comparing non-optional value of type Any to nil always returns false

What's the difference between testing an optional for nil and .None ?

In normal usage there's no difference, they are interchangeable. .None is an enum representation of the nil (absence of) value implemented by the Optional<T> enum.

The equivalence of .None and nil is thanks to the Optional<T> type implementing the NilLiteralConvertible protocol, and an overload of the != and == operators.

You can implement that protocol in your own classes/structs/enums if you want nil to be assignable, such as:

struct MyStruct : NilLiteralConvertible {
static func convertFromNilLiteral() -> MyStruct {
return MyStruct() // <== here you should provide your own implementation
}
}

var t: MyStruct = nil

(note that the variable is not declared as optional)

When should I compare an optional value to nil?

It is almost always unnecessary to check if an optional is not nil. Pretty much the only time you need to do this is if its nil-ness is the only thing you want to know about – you don’t care what’s in the value, just that it’s not nil.

Under most other circumstances, there is a bit of Swift shorthand that can more safely and concisely do the task inside the if for you.

Using the value if it isn’t nil

Instead of:

let s = "1"
let i = Int(s)

if i != nil {
print(i! + 1)
}

you can use if let:

if let i = Int(s) {
print(i + 1)
}

You can also use var:

if var i = Int(s) {
print(++i) // prints 2
}

but note that i will be a local copy - any changes to i will not affect the value inside the original optional.

You can unwrap multiple optionals within a single if let, and later ones can depend on earlier ones:

if let url = NSURL(string: urlString),
data = NSData(contentsOfURL: url),
image = UIImage(data: data)
{
let view = UIImageView(image: image)
// etc.
}

You can also add where clauses to the unwrapped values:

if let url = NSURL(string: urlString) where url.pathExtension == "png",
let data = NSData(contentsOfURL: url), image = UIImage(data: data)
{ etc. }

Replacing nil with a default

Instead of:

let j: Int
if i != nil {
j = i
}
else {
j = 0
}

or:

let j = i != nil ? i! : 0

you can use the nil-coalescing operator, ??:

// j will be the unwrapped value of i,
// or 0 if i is nil
let j = i ?? 0

Equating an optional with a non-optional

Instead of:

if i != nil && i! == 2 {
print("i is two and not nil")
}

you can check if optionals are equal to non-optional values:

if i == 2 {
print("i is two and not nil")
}

This also works with comparisons:

if i < 5 { }

nil is always equal to other nils, and is less than any non-nil value.

Be careful! There can be gotchas here:

let a: Any = "hello"
let b: Any = "goodbye"
if (a as? Double) == (b as? Double) {
print("these will be equal because both nil...")
}

Calling a method (or reading a property) on an optional

Instead of:

let j: Int
if i != nil {
j = i.successor()
}
else {
// no reasonable action to take at this point
fatalError("no idea what to do now...")
}

you can use optional chaining, ?.:

let j = i?.successor()

Note, j will also now be optional, to account for the fatalError scenario. Later, you can use one of the other techniques in this answer to handle j’s optionality, but you can often defer actually unwrapping your optionals until much later, or sometimes not at all.

As the name implies, you can chain them, so you can write:

let j = s.toInt()?.successor()?.successor()

Optional chaining also works with subscripts:

let dictOfArrays: ["nine": [0,1,2,3,4,5,6,7]]
let sevenOfNine = dictOfArrays["nine"]?[7] // returns {Some 7}

and functions:

let dictOfFuncs: [String:(Int,Int)->Int] = [
"add":(+),
"subtract":(-)
]

dictOfFuncs["add"]?(1,1) // returns {Some 2}

Assigning to a property on an optional

Instead of:

if splitViewController != nil {
splitViewController!.delegate = self
}

you can assign through an optional chain:

splitViewController?.delegate = self

Only if splitViewController is non-nil will the assignment happen.

Using the value if it isn’t nil, or bailing (new in Swift 2.0)

Sometimes in a function, there’s a short bit of code you want to write to check an optional, and if it’s nil, exit the function early, otherwise keep going.

You might write this like this:

func f(s: String) {
let i = Int(s)
if i == nil { fatalError("Input must be a number") }
print(i! + 1)
}

or to avoid the force unwrap, like this:

func f(s: String) {
if let i = Int(s) {
print(i! + 1)
}
else {
fatalErrr("Input must be a number")
}
}

but it’s much nicer to keep the error-handling code at the top by the check. This can also lead to unpleasant nesting (the "pyramid of doom").

Instead you can use guard, which is like an if not let:

func f(s: String) {
guard let i = Int(s)
else { fatalError("Input must be a number") }

// i will be an non-optional Int
print(i+1)
}

The else part must exit the scope of the guarded value, e.g. a return or fatalError, to guarantee that the guarded value will be valid for the remainder of the scope.

guard isn’t limited to function scope. For example the following:

var a = ["0","1","foo","2"]
while !a.isEmpty {
guard let i = Int(a.removeLast())
else { continue }

print(i+1, appendNewline: false)
}

prints 321.

Looping over non-nil items in a sequence (new in Swift 2.0)

If you have a sequence of optionals, you can use for case let _? to iterate over all the non-optional elements:

let a = ["0","1","foo","2"]
for case let i? in a.map({ Int($0)}) {
print(i+1, appendNewline: false)
}

prints 321. This is using the pattern-matching syntax for an optional, which is a variable name followed by ?.

You can also use this pattern matching in switch statements:

func add(i: Int?, _ j: Int?) -> Int? {
switch (i,j) {
case (nil,nil), (_?,nil), (nil,_?):
return nil
case let (x?,y?):
return x + y
}
}

add(1,2) // 3
add(nil, 1) // nil

Looping until a function returns nil

Much like if let, you can also write while let and loop until nil:

while let line = readLine() {
print(line)
}

You can also write while var (similar caveats to if var apply).

where clauses also work here (and terminate the loop, rather than skipping):

while let line = readLine() 
where !line.isEmpty {
print(line)
}

Passing an optional into a function that takes a non-optional and returns a result

Instead of:

let j: Int
if i != nil {
j = abs(i!)
}
else {
// no reasonable action to take at this point
fatalError("no idea what to do now...")
}

you can use optional’s map operator:

let j = i.map { abs($0) }

This is very similar to optional chaining, but for when you need to pass the non-optional value into the function as an argument. As with optional chaining, the result will be optional.

This is nice when you want an optional anyway. For example, reduce1 is like reduce, but uses the first value as the seed, returning an optional in case the array is empty. You might write it like this (using the guard keyword from earlier):

extension Array {
func reduce1(combine: (T,T)->T)->T? {

guard let head = self.first
else { return nil }

return dropFirst(self).reduce(head, combine: combine)
}
}

[1,2,3].reduce1(+) // returns 6

But instead you could map the .first property, and return that:

extension Array {
func reduce1(combine: (T,T)->T)->T? {
return self.first.map {
dropFirst(self).reduce($0, combine: combine)
}
}
}

Passing an optional into a function that takes an optional and returns a result, avoiding annoying double-optionals

Sometimes, you want something similar to map, but the function you want to call itself returns an optional. For example:

// an array of arrays
let arr = [[1,2,3],[4,5,6]]
// .first returns an optional of the first element of the array
// (optional because the array could be empty, in which case it's nil)
let fst = arr.first // fst is now [Int]?, an optional array of ints
// now, if we want to find the index of the value 2, we could use map and find
let idx = fst.map { find($0, 2) }

But now idx is of type Int??, a double-optional. Instead, you can use flatMap, which “flattens” the result into a single optional:

let idx = fst.flatMap { find($0, 2) }
// idx will be of type Int?
// and not Int?? unlike if `map` was used

Swift: Testing optionals for nil

In Xcode Beta 5, they no longer let you do:

var xyz : NSString?

if xyz {
// Do something using `xyz`.
}

This produces an error:

does not conform to protocol 'BooleanType.Protocol'

You have to use one of these forms:

if xyz != nil {
// Do something using `xyz`.
}

if let xy = xyz {
// Do something using `xy`.
}

What is an optional value in Swift?

An optional in Swift is a type that can hold either a value or no value. Optionals are written by appending a ? to any type:

var name: String? = "Bertie"

Optionals (along with Generics) are one of the most difficult Swift concepts to understand. Because of how they are written and used, it's easy to get a wrong idea of what they are. Compare the optional above to creating a normal String:

var name: String = "Bertie" // No "?" after String

From the syntax it looks like an optional String is very similar to an ordinary String. It's not. An optional String is not a String with some "optional" setting turned on. It's not a special variety of String. A String and an optional String are completely different types.

Here's the most important thing to know: An optional is a kind of container. An optional String is a container which might contain a String. An optional Int is a container which might contain an Int. Think of an optional as a kind of parcel. Before you open it (or "unwrap" in the language of optionals) you won't know if it contains something or nothing.

You can see how optionals are implemented in the Swift Standard Library by typing "Optional" into any Swift file and ⌘-clicking on it. Here's the important part of the definition:

enum Optional<Wrapped> {
case none
case some(Wrapped)
}

Optional is just an enum which can be one of two cases: .none or .some. If it's .some, there's an associated value which, in the example above, would be the String "Hello". An optional uses Generics to give a type to the associated value. The type of an optional String isn't String, it's Optional, or more precisely Optional<String>.

Everything Swift does with optionals is magic to make reading and writing code more fluent. Unfortunately this obscures the way it actually works. I'll go through some of the tricks later.

Note: I'll be talking about optional variables a lot, but it's fine to create optional constants too. I mark all variables with their type to make it easier to understand type types being created, but you don't have to in your own code.



How to create optionals

To create an optional, append a ? after the type you wish to wrap. Any type can be optional, even your own custom types. You can't have a space between the type and the ?.

var name: String? = "Bob" // Create an optional String that contains "Bob"
var peter: Person? = Person() // An optional "Person" (custom type)

// A class with a String and an optional String property
class Car {
var modelName: String // must exist
var internalName: String? // may or may not exist
}


Using optionals

You can compare an optional to nil to see if it has a value:

var name: String? = "Bob"
name = nil // Set name to nil, the absence of a value
if name != nil {
print("There is a name")
}
if name == nil { // Could also use an "else"
print("Name has no value")
}

This is a little confusing. It implies that an optional is either one thing or another. It's either nil or it's "Bob". This is not true, the optional doesn't transform into something else. Comparing it to nil is a trick to make easier-to-read code. If an optional equals nil, this just means that the enum is currently set to .none.



Only optionals can be nil

If you try to set a non-optional variable to nil, you'll get an error.

var red: String = "Red"
red = nil // error: nil cannot be assigned to type 'String'

Another way of looking at optionals is as a complement to normal Swift variables. They are a counterpart to a variable which is guaranteed to have a value. Swift is a careful language that hates ambiguity. Most variables are define as non-optionals, but sometimes this isn't possible. For example, imagine a view controller which loads an image either from a cache or from the network. It may or may not have that image at the time the view controller is created. There's no way to guarantee the value for the image variable. In this case you would have to make it optional. It starts as nil and when the image is retrieved, the optional gets a value.

Using an optional reveals the programmers intent. Compared to Objective-C, where any object could be nil, Swift needs you to be clear about when a value can be missing and when it's guaranteed to exist.



To use an optional, you "unwrap" it

An optional String cannot be used in place of an actual String. To use the wrapped value inside an optional, you have to unwrap it. The simplest way to unwrap an optional is to add a ! after the optional name. This is called "force unwrapping". It returns the value inside the optional (as the original type) but if the optional is nil, it causes a runtime crash. Before unwrapping you should be sure there's a value.

var name: String? = "Bob"
let unwrappedName: String = name!
print("Unwrapped name: \(unwrappedName)")

name = nil
let nilName: String = name! // Runtime crash. Unexpected nil.


Checking and using an optional

Because you should always check for nil before unwrapping and using an optional, this is a common pattern:



Related Topics



Leave a reply



Submit