1

How does one check if a Swift array contains a particular instance of an object? Consider this simple example:

class Car {}

let mazda = Car()
let toyata = Car()

let myCars = [mazda, toyata]

myCars.contains(mazda) // ERROR!

My investigations have let me to the conclusion that the Car class must adopt the Equatable protocol. It seems to be the case:

class Car: Equatable {
    static func ==(lhs: Car, rhs: Car) -> Bool {
        return true
    }
}

Then myCars.contains(mazda) does indeed return true.

However, the implementation of == is obviously not what I want. What I really want it to return is the answer to the question: Are lhs and rhs the same Car instances?

Is it really that complicated?

Thanks!

Michael Knudsen
  • 629
  • 1
  • 7
  • 23
  • *"Are lhs and rhs the same Car instances?"* - No, the question should be "do lhs and rhs represent the same equivalent Cars?". They don't need to be the same instances to be equal. – rmaddy Oct 16 '16 at 18:01
  • No, that is not what I want. In my case (not the toy example with cars), I really do want to know if they are the same instance. Anyway, problem solved with the === operator. Never heard of that one before. – Michael Knudsen Oct 16 '16 at 18:10
  • That's not how it is supposed to work. You are violating the semantics of the `Equatable` protocol (and hence the Hashable protocol). You are going to cause some strange, very hard to track bugs in your code. – rmaddy Oct 16 '16 at 18:12

2 Answers2

3
=== 

operator in Swift checks whether two object references both refer to the same object instance.

So your Equatable could look like this:

   static func ==(lhs: Car, rhs: Car) -> Bool {
        return lhs===rhs
    }

I do not recommend this approach. Equatable's purpose is not to evaluate references to the same object.

Marcos Griselli
  • 1,306
  • 13
  • 22
  • Don't do this. It violates the semantics of the Equatable (and Hashable) protocols. – rmaddy Oct 16 '16 at 18:13
  • I agree with you that it somehow feels wrong to use Equatable. I am (trying to) make a simple implementation of an undirected graph. Basically just a list of vertices, and I want to ask: Does my graph contain a particular vertex? – Michael Knudsen Oct 16 '16 at 18:13
  • But why does it need to be the same reference? Vertices properties can be compared by x and y values. @vadian provides an example above of equality property comparison. – Marcos Griselli Oct 17 '16 at 23:19
2

Use the identity operator

static func ==(lhs: Car, rhs: Car) -> Bool {
   return lhs === rhs
}

Or even without Equatable

myCars.contains{ $0 === mazda }

Edit: But a better solution is to implement equality rather than identity, for example a vendor and type property in the class

static func ==(lhs: Car, rhs: Car) -> Bool {
   return lhs.vendor == rhs.vendor && lhs.type == rhs.type
}
vadian
  • 274,689
  • 30
  • 353
  • 361
  • 1
    Don't do this. It violates the semantics of the Equatable (and Hashable) protocols. – rmaddy Oct 16 '16 at 18:13
  • @rmaddy I know, but the OP wants a "cheap" solution. – vadian Oct 16 '16 at 18:15
  • What else could you do, then? I have a Graph class which is basically an array of Vertex objects. How would you answer: Does the Graph object contain a specific Vertex? – Michael Knudsen Oct 16 '16 at 18:15
  • Nope, I don't want a cheap solution. I want a good solution :-) – Michael Knudsen Oct 16 '16 at 18:15
  • @MichaelKnudsen If you want a good solution, don't implement `==` by using `===`. Start by updating your question with your actual code instead of the fake "Car" sample. The `==` must check if two objects are equivalent, not the same instance. You do this by comparing property values. – rmaddy Oct 16 '16 at 18:18
  • If there are properties in the class, compare them. – vadian Oct 16 '16 at 18:19