0x416e746f6e
0x416e746f6e

Reputation: 10136

When Set.insert() returns oldMember that is distinguishable from newMember?

The documentation for Set's insert method states:

Return Value

(true, newMember) if newMember was not contained in the set. If an element equal to newMember was already contained in the set, the method returns (false, oldMember), where oldMember is the element that was equal to newMember.

In some cases, oldMember may be distinguishable from newMember by identity comparison or some other means.

What are the cases when oldMember might be distinguishable from newMember they are talking about? Any example?

Reason for the question: I have a Set that contains objects that are not necessarily Hashable. I had the idea to use Unmanaged.passUnretained(object).toOpaque().hashValue to get a hash-value for just any object (basically from its address) in a wrapper struct, however the above bit in the documentation makes me wary.

Upvotes: 1

Views: 131

Answers (1)

Hamish
Hamish

Reputation: 80901

If you have some way to tell two instances apart even when they compare equal by their implementation of Equatable's == operator, then you can use this to compare the element to insert with the memberAfterInsert returned by insert(_:).

A prime example of this, as the documentation says, is the identity comparison (===). Two object instances that compare equal may not necessarily be the exact same instance (i.e have the same address).

For example:

class Foo : Hashable {

    static func ==(lhs: Foo, rhs: Foo) -> Bool {
        return lhs.foo == rhs.foo
    }

    var hashValue: Int {
        return foo
    }

    let foo: Int

    init(_ foo: Int) {
        self.foo = foo
    }
}

var set : Set = [Foo(10)]

let toInsert = Foo(10)
let result = set.insert(toInsert) // the element toInsert will compare equal to the element
                                  // already in the set, thus the element already in the
                                  // already in the set will be returned.

print(result) // (false, Foo) – false, as element was already in set
print(result.memberAfterInsert == toInsert) // true – obviously they were equal.
print(result.memberAfterInsert === toInsert) // false – but they weren't the same instance.

In your case of using a hash value based on the identity, as long as your Equatable implementation also relies on the identity, you won't break the contract of Hashable (instances that compare equal with == must have the same hashValue) – although obviously you won't be able to use the identity to differentiate the memberAfterInsert with the element you are trying to insert.

However it's worth noting that having a Set where the Element's equality implementation is solely based on identity may cause surprising results for objects that look like they should be equivalent. For example, it would be perfectly fine to have a set of [Foo(5), Foo(5), Foo(5), Foo(5)], given that the objects are different instances.

Upvotes: 1

Related Questions