When you write to that subscript (setter), it works like normal subscript. The difference is when you read from it. If the value exists, it returns the value, otherwise, it evaluate closure and return the result.
So if the value exist, say letterCount[letter] == 4, it'd just return 4, get incremented (by += 1), then is assigned back to the letterCount[letter, default: 0] (remember, when writing, this works like normal subscript). In this case, you're writing 5 into the subscript.
If the value doesn't exist, it'll evaluate the default closure (0 in this case), then the value gets incremented, then assigned back to the subscript. In this case, you're writing 1 into the subscript.
This design does have advantage that the getter remains non-mutating, but I digress.
It's a neat inout trick. Let's first name the unnamed (closure).
func doTrick<T>(_ t: inout T) -> T {
return t
}
...
return doTrick(&self[key, default: value()])
The semantic of Swift regarding its inout variable, is the copy-in-copy-out (if only I remember the source). So what happens, is that, when you call `doSomething(&x)
- you read
x
- make a mutable copy
- pass the copy into
doSomething, and let it mutate the copy
- *copy the new value of the copy back to
x
- *return the new value
Of course compiler can optimise away a lot of things here, but it needs to make it appear to behave like this.
So when you call it doSomething(&self[key, default: value]),
- you read
self[key, default: value], triggering the subscript getter
- make a mutable copy
- pass the copy into
doSomething, and let it mutate (which it does nothing)
- copy the new value of the copy back to
self[key, default: value], triggering the subscript getter
The first and the last one is important, and is what we need.
You need to read from self[...], then assign to it. Because if the value doesn't exist in the dictionary, the new value is not written back (as per documentation).
So you're essentially doing this:
let copy = self[key, default: value()]
let result = copy
self[key, default: value()]
return result
in one line.
* Not sure which of the two comes first. It doesn't matter here anyway.