Problem with struct in Dictionary

Hello, I have a problem with using struct in Dictionary. Please take a look at this code first.

import Foundation

struct COWStruct {
    
    class COWStorageClass { }
    
    var s = COWStorageClass()
    
    mutating func mutateSomething() {
        print(isKnownUniquelyReferenced(&self.s) ? "unique" : "not unique")
    }
}

var dict: [Int: COWStruct] = [:]

dict[0] = COWStruct()

let keysArray = dict.keys.map { $0 }

for key in keysArray {
    dict.values[dict.index(forKey: key)!].mutateSomething() // not unique
}

for key in keysArray {
    dict[key]?.mutateSomething() // not unique
}

for key in keysArray {
    dict[key]!.mutateSomething() // not unique
}

It’s simple. There is a struct with one mutating function to test that the storage property is uniquely referenced. This is used as a value in dict [Int: COWStruct]

As you see, all the mutating codes are failing to get the storage property uniquely referenced. They all print not unique

This problem occurs in Xcode 9.3, Swift 4.1

Am I doing something wrong?

2 Likes

The dictionary seems to be copying the object for some reason, because it works with an array:

struct COWStruct {
    
    class COWStorageClass { }
    
    var s = COWStorageClass()
    
    mutating func mutateSomething() {
        print(isKnownUniquelyReferenced(&self.s) ? "unique" : "not unique")
    }
}

var dict = [0 : COWStruct()] 
var array: [COWStruct?] = [COWStruct()]

dict[0]?.mutateSomething() // not unique
array[0]?.mutateSomething() // unique

There's some discussion of this behaviour on this Stack Overflow Q&A:

The main issue is that currently Dictionary's subscript(_:) uses a getter + setter (in contrast to Array's subscript(_:) which uses a getter + addressor).

There are some workarounds to this however, such as using Dictionary's subscript(_:default:) which (as of Swift 4.1) uses an addressor, or temporarily removing the value you want to mutate before re-inserting it into the dictionary.

It would be really nice if we could find a way to make Dictionary's subscript(_:) work with addressors though.

2 Likes

This is something we’re hoping to do with generalized accessors from the ownership manifesto.

2 Likes