Equatable for structs: compare all fields?

When implementing the Equality protocol for a struct: is it recommended to always compare all of the structs fields? Or is it ok to ignore some fields of a struct in the comparison?


Eg:

struct ControlPoint : Equatable {
  let position: Vec2
  let pointId: Int
  let ownerId : Int
}

func ==(lhs: ControlPoint, rhs: ControlPoint) -> Bool {
  return lhs.pointId == rhs.pointId  && lhs.ownerId == rhs.ownerId // ignoring the position
}


In my case I do not really care about the position. I have some old ControlPoint and some new ControlPoint and I want to check if they represent the same internal point. But the pointId and ownerId itself are implementation details which I do not want to expose ie it do not want the client of the class to compare both of them manually

Is using the Equality protocol the right thing to do in this situation or is it confusing for the client that two structs with different values are treated the same?


Is it better two create my own protocol like this?:

protocol PointEquality {
    public func areSamePoint(lhs: Self, rhs: Self) -> Bool
}

struct ControlPoint : PointEquality {
  let position: Vec2
  let pointId: Int
  let ownerId : Int
}
func areSamePoint(lhs: ControlPoint, rhs: ControlPoint) -> Bool {
  return lhs.pointId == rhs.pointId  && lhs.ownerId == rhs.ownerId // ignoring the position
}



When using classes it seems more obvious that a class can have some internal state which is not considered in the equality check. But with structs this seems weird to me. But I do not like using a class for the ControlPoint either because it has no mutable fields and it is only created, used for rendering and hittesting and thrown away ie has no lifecycle.

It seems fine to me, though it might come as a bit of a surprise to later readers of the code.


If you use Equatable, there should be few or no conceivable cases in which you would want to take account of the position when assessing equality.

Is using the Equality protocol the right thing to do in this situation or is it confusing for the client that two structs with different values are treated the same?

I think the key point is “is it confusing for the clients”. Do you think it’ll be confusing? It’s hard for us to say without knowing more about how your various interfaces fit together.

There are lots of situations where equality ignores some state. Consider a mutable array implementation. When you add an element to the array, you want to grow the array in chunks to improve efficiency. So, the array has both a ‘count’ (the actual number of elements in the array) and a ‘capacity’ (the size of the array’s buffer). No one would ever suggest that the array include ‘capacity’ in its equality determination.

This example is easy because ‘capacity’ is internal state. However, there are other examples that are less clear cut. Consider a string implementation. It’s likely that you want it to be able to represents multiple different strings that all compare as equal (for example,

"na\u{ef}ve"
and
"nai\u{0308}ve"
).

So, it’s perfectly reasonable for different data to compare the same, so your question really boils down to what is best for the users of your API.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

No, you don't need to compare all fields when implementing Equatable.


That's actually the entire reason Equatable is overridable - because the question of "are these two things equivalent?" depends on the context of your application. As you said, you consider two ControlPoints equivalent even if they have different positions. If that's what makes sense for your application, do it.


== is the equivalence operator (even though it's called "equals", it's best to think of it as "equivalent"), === is the identity comparison operator (which is more of a literal "equals").

Equatable for structs: compare all fields?
 
 
Q