Setting value to Dictionary with Optional value


(Sungmin Kim) #1

Hello, I have a question on using dictionary with optional type value. Take a look at this code please.

// Swift 4.2
var dict: [String: Int?] = ["a": 1]

do {
    dict["a"] = Optional<Int>.none
    
    print(dict.count) // 1 (Optional value is set)
}

dict["a"] = 1 // set it back.

do {
    dict["a"] = .none
    
    print(dict.count) // 0 (Value is removed)
}

As you can see, setting .none removes the value while Optional<Int>.none does not. That means I cannot just simply omit the long type name believing that the compiler would infer what I intended.

I guess .none is regarded same as nil but I think those two cases should behave in the same way. Otherwise it would be very easy to misuse.


(Ben Rimmington) #2

You can also use .some(nil) or .some(.none) instead of Optional<Int>.none.


(Ben Rimmington) #3

The double-optional is mentioned in
SR-2176: Add warning for ambiguous enum value assignment.


#4

If you're assigning to a dictionary subscript expression:

dict["a"] = …

use an expression of the value type to replace the existing value:

dict["a"] = nil as Int? // replaces

To remove the existing value, use an expression of the value type with an extra level of optionality, so:

dict["a"] = nil as Int?? // removes

Now, normal compiler inference can deduce the type of the RHS from the type of the LHS, so the second of these can be simplified:

dict["a"] = nil // removes, nil inferred to be of type 'Int??'

That gives you a nice dichotomy:

dict["a"] = nil as Int? // replaces
dict["a"] = nil // removes

which you can just learn as a rule of thumb for value types that are optionals.


(Hamish Knight) #5

You can also use the sugared version of Optional<Int>Int?. For example:

dict["a"] = Int?.none

Or alternatively use updateValue(_:forKey:) which can only replace, and not remove, values:

dict.updateValue(nil, forKey: "a")

Though generally, I would advise against using dictionaries with optional Value types – IMO it's far too easy to shoot yourself in the foot with doubly wrapped optionals.