Consider the following example involving the nil coalescing operator ??
:
let mysteryInc = ["Fred": "Jones", "Scooby-Doo": nil]
let lastname = mysteryInc["Scooby-Doo"] ?? "no last name"
print(lastname == nil) // true
As shown by the last print
statement, the result of the nil coalescing operator is nil
.
If the nil coalescing operator is supposed to unwrap an Optional, why is it returning nil
?
To see what is going on here, assign the dictionary look up to a constant:
let name = mysteryInc["Scooby-Doo"]
print(type(of: name))
Output:
Optional<Optional<String>>
So, name
is a double Optional, a String??
.
When the nil coalescing operator is applied, it unwraps the outer Optional and leaves an Optional<String>
(aka String?
). In the example in the question, Swift treats the String literal "no last name"
as type String?
so that ??
can be applied.
If you examine the value of name
you will find that it is Optional(nil)
. All dictionary look ups return Optionals because the key might not exist (in which case they return nil
). In this case, the mysteryInc
dictionary is of type [String: String?]
. The value corresponding to "Scooby-Doo"
is nil
which then gets wrapped into an Optional (because of the dictionary look up) to become Optional(nil)
.
Finally, the value Optional(nil)
is unwrapped by ??
. Since Optional(nil) != nil
, Swift unwraps Optional(nil)
and returns nil
instead of returning the expected "no last name"
.
And that is how you can get nil
from the nil coalescing operator. It did unwrap the Optional; there just happened to be a nil
inside of that Optional.
As @LeoDabus noted in the comments, the correct way to deal with this situation is to conditionally cast the name to String
before applying the nil coalescing operator:
let lastname = mysteryInc["Scooby-Doo"] as? String ?? "no last name"
In reality, it is best to avoid having Optionals as values for your dictionary for the very reason that @Sulthan raises:
What does mysteryInc["Fred"] = nil
do?
Assigning nil
to a dictionary entry removes that entry from the dictionary, so mysteryInc["Fred"] = nil
doesn't replace Optional("Jones")
with nil
, it removes the "Fred"
entry altogether. To leave "Fred"
in the dictionary and make his last name nil
, you need to assign mysteryInc["Fred"] = Optional(nil)
or mysteryInc.updateValue(nil, forKey: "Fred")
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With