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 Optional
– nil
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.
Any
is an opaque box that can hold anything. You can gleam what's inside by trying to cast it usingas?
.Optional<Wrapped>
is a transparent box. It lets you see that its contents are either a value ofOptional<Wrapped>.some(wrapped)
orOptional<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
tonil
always returnsfalse
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 nil
s, 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
Ios-Charts Set Maximum Visible X Axis Values
Get a List of Nodes in an Specific Area
Create Complicated Nscompoundpredicate in Swift 3
If-Let Any to Rawrepresentable<String>
Value of Type 'Tags' Has No Member 'Lastused'
How How to Perform Multiple Alamofire Requests That Are Finished One After Another
Swift 'Unable to Dequeue a Cell with Identifier Intervalcellidentifier
How to Check If a Type Is Optional in Swift
How to Find the Time Interval Remaining from Nstimer
Is There a Number Type with Bigger Capacity Than U_Long/Uint64 in Swift
How to Get Title from Wkinterfacebutton
Which Value Types in Swift Supports Copy-On-Write
Can You Enforce a Typealias in Swift
Deep Copy of Cmimagebuffer or Cvimagebuffer
Swift Callkit Sometimes Can't Activate Loudspeaker After Received Call (Only Incoming Call)