Swift Arrays and Dictionaries
Clearing my confusion
While reading The Swift Programming Language, I came across a couple of sections like Collection Types and Classes and Structures: Assignment and Copy Behavior for Collection Types
The obvious first issue is
Immutability has a slightly different meaning for arrays, however. You are still not allowed to perform any action that has the potential to change the size of an immutable array, but you are allowed to set a new value for an existing index in the array. This enables Swift’s Array type to provide optimal performance for array operations when the size of an array is fixed.
I think Apple has heard from everyone how crazy it is to allow mutation of a supposedly immutable array so long as you don't change the length. I fully expect to see a fixed size array in a future update for the optimizations that allows, along with making a let
array truly immutable.
But let's focus on something else that was highly confusing at first:
Whenever you assign a Dictionary instance to a constant or variable, or pass a Dictionary instance as an argument to a function or method call, the dictionary is copied at the point that the assignment or call takes place.
...
If the keys and/or values stored in the Dictionary instance are value types (structures or enumerations), they too are copied when the assignment or call takes place.
This seems to be saying that a dictionary is stack allocated and copied every time you pass it anywhere or assign it to a variable.
After much confusion and discussion with some Apple folks, here's how Dictionary<K,V> actually works:
- Dictionaries declared
let
are constant in both size and keys. Values are also constant if V is a value type. If V is a reference type then the underlying value object can still be modified. (If you use mutating keys then you deserve what you get.) - Dictionaries declared
var
are mutable. - In either case, the hash buckets and value storage are heap allocated.
- Passing a dictionary does copy the dictionary, but it's just copying a small header struct so the copy is almost free.
- When the guide talks about copying the dictionary, it means the program logically behaves as if everyone has their own copy of the dictionary; in reality, they are copy-on-write and the header structs all point to the same hash buckets until someone ruins the party by mutating
Array<T> works the same way, except as noted above that you can currently mutate a constant array as long as you don't change its size.
You can avoid the copying behavior entirely by passing the objects around by reference. Declare the parameter inout
, then pass the var
with an ampersand.
func makeIt(inout a: Dictionary<String, String>) {}
...
var d = ["a":"b", "c":"d"]
makeIt(&d)
Hopefully this helps clear up confusion. I realize now the information was available, I was just carrying too many biases from my experience with other languages and making unwarranted assumptions about how Swift actually works.
This blog represents my own personal opinion and is not endorsed by my employer.