Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assign value to optional dictionary in Swift

I'm finding some surprising behavior with optional dictionaries in Swift.

var foo:Dictionary<String, String>?

if (foo == nil) {
    foo = ["bar": "baz"]
}
else {
    // Following line errors with "'Dictionary<String, String>?' does
    // not have a member named 'subscript'"
    foo["qux"] = "quux"
}

I've played with this a lot, trying to figure out what I might be missing, but nothing seems to make this code work as expected short of making the dictionary not optional. What am I missing?

The closest I can get is the following, but of course it's ridiculous.

var foo:Dictionary<String, String>?

if (foo == nil) {
    foo = ["bar": "baz"]
}
else if var foofoo = foo {
    foofoo["qux"] = "quux"
    foo = foofoo
}
like image 582
Garrett Albright Avatar asked Jun 07 '14 10:06

Garrett Albright


People also ask

How do I set optional value in Swift?

An optional value either contains a value or contains nil to indicate that the value is missing. Write a question mark ( ? ) after the type of a value to mark the value as optional.

Is dictionary value type in Swift?

In Swift, Array, String, and Dictionary are all value types. They behave much like a simple int value in C, acting as a unique instance of that data. You don't need to do anything special — such as making an explicit copy — to prevent other code from modifying that data behind your back.

Is optional data type in Swift?

However there is another data type in Swift called Optional, whose default value is a null value ( nil ). You can use optional when you want a variable or constant contain no value in it. An optional type may contain a value or absent a value (a null value). Non technically, you can think optional as a shoe box.

Why do we use optional in Swift?

Optional types or Optionals in Swift You use optionals in situations where a value may be absent. An optional represents two possibilities: Either there is a value, and you can unwrap the optional to access that value, or there isn't a value at all. That's a pretty straightforward definition.


2 Answers

The lightbulb moment is when you realize that an Optional dictionary is not a Dictionary. An Optional anything is not that thing! It is an Optional!! And that's all it is. Optional is itself a type. An Optional is just an enum, wrapping the possible cases nil and some value. The wrapped value is a completely different object, stored inside.

So an Optional anything does not act like the type of that thing. It is not that thing! It is just an Optional. The only way to get at the thing is to unwrap it.

The same is true of an implicitly unwrapped Optional; the difference is just that the implicitly unwrapped Optional is willing to produce (expose) the wrapped value "automatically". But it is still, in fact, wrapped. And, as Bryan Chen has observed, it is wrapped immutably; the Optional is just holding it for you - it is not giving you a place to play with it.

like image 72
matt Avatar answered Oct 30 '22 14:10

matt


you can use this code

if var foofoo = foo {
    foofoo["qux"] = "quux"
    foo = foofoo
} else {
    foo = ["bar": "baz"]    
}

with this code

var foo:Dictionary<String, String>? = Dictionary()
foo[""]=""

error: 'Dictionary<String, String>?' does not have a member named 'subscript'
foo[""]=""
^

the error message makes sense to me that Dictionary<String, String>? does not implement subscript method, so you need to unwrap it before able to use subscript.

one way to call method on optional is use ! i.e. foo![""], but...

var foo:Dictionary<String, String>? = Dictionary()
foo![""]=""

error: could not find member 'subscript'
foo![""]=""
~~~~~~~~^~~

whereas

var foo:Dictionary<String, String>? = Dictionary()
foo![""]

works


it is interesting these code failed to compile

var foo:Dictionary<String, String>! = Dictionary() // Implicitly unwrapped optional
foo[""]=""

error: could not find an overload for 'subscript' that accepts the supplied arguments
foo[""]=""
~~~~~~~^~~

var foo:Dictionary<String, String>! = Dictionary() // Implicitly unwrapped optional
foo.updateValue("", forKey: "")

immutable value of type 'Dictionary<String, String>' only has mutating members named 'updateValue'
foo.updateValue("", forKey: "")
^   ~~~~~~~~~~~

the last error message is most interesting, it is saying the Dictionary is immutable, so updateValue(forKey:) (mutating method) can't be called on it

so what happened is probably that the Optional<> store the Dictionary as immutable object (with let). So even Optional<> it is mutable, you can't modify the underlying Dictionary object directly (without reassign the Optional object)


and this code works

class MyDict
{
    var dict:Dictionary<String, String> = [:]

    subscript(s: String) -> String? {
        get {
            return dict[s]
        }
        set {
            dict[s] = newValue
        }
    }
}

var foo:MyDict? = MyDict()
foo!["a"] = "b" // this is how to call subscript of optional object

and this lead me to another question, why Array and Dictionary are value type (struct)? opposite to NSArray and NSDictionary which are reference type (class)

like image 38
Bryan Chen Avatar answered Oct 30 '22 15:10

Bryan Chen