I am wondering if someone can explain the behavior I'm seeing in this code:
class Node {
let value: Int
var children: [Int : Node]
init(_ value: Int) {
self.value = value
self.children = [:]
}
subscript(_ index: Int) -> Node? {
get { children[index] }
set { children[index] = newValue }
}
}
var node1 = Node(1)
node1[2] = Node(2)
print(isKnownUniquelyReferenced(&node1)) // true
print(isKnownUniquelyReferenced(&node1[2])) // false
var node2 = Node(1)
node2.children[2] = Node(2)
print(isKnownUniquelyReferenced(&node2)) // true
print(isKnownUniquelyReferenced(&node2.children[2])) // true
It seems like the subscript setter is maintain a strong reference to the instance of
Node.
Nevin
2
I’m not an expert here, but I notice two things.
First, it does not matter how you set the values. In your example, whether you write node[2] = Node(2) or node.children[2] = Node(2), the behavior is the same. All that matters is what you pass to isKnownUniquelyReferenced. In both cases we have:
isKnownUniquelyReferenced(&node[2]) // false
isKnownUniquelyReferenced(&node.children[2]) // true
Next, it is indeed the subscript setter which results in the behavior difference. Replacing the set block with a _modify block makes both calls to isKnownUniquelyReferenced return true. The get block may be left as-is, or replaced with a _read block. I’m not certain what difference it makes:
subscript(_ index: Int) -> Node? {
_read { yield children[index] }
_modify { yield &children[index] }
}
Of course, both _read and _modify are undocumented and not officially part of the language. They are, however, used extensively in the standard library.
1 Like
Thank you, this led me down a new path, and I found this:
https://github.com/apple/swift/blob/main/docs/OwnershipManifesto.md#generalized-accessors
I don’t know whether these accessors are part of the language, but I’m going to play with it when I return to my desk.
i have been using _read and _modify (_modify more than _read) in production for years and i know many other swift developers who do the same. to me, they are part of the language already, as it’s very difficult to write performant Swift libraries without _modify.
i don’t know why they haven’t been naturalized yet, they are so widely used that changing the semantics would break a ton of libraries. the situation is a lot like @_inlinable before it lost the underscore.