26

I am reading the Rust book and trying to understand use cases for PartialEq and Eq traits.

I realise that PartialEq is for relations which are not necessarily reflexive (i.e. there can be such x that x != x) and that Eq is a marker trait which says that relation is also reflexive (and now it is a proper equivalence relation).

The books gives an example where PartialEq is not enough and Eq is required: HashMap<K, V> lookups. Indeed, if we use as a key a data type which only implements PartialEq (for example floating point number), we would get in trouble when we try to use NaN as a key, since we won't be able to find it.

Now, I am trying to understand what feature of a lookup makes it require Eq. I may be able to understand it better if I find an example of code which does not require Eq.

The book says that assert_eq! requires only PartialEq so that we are able to compare things for equality. But if we write assert_eq!(f64::NAN, some_code_producing_nan()); in a test, the test will always fail. We have the same basic issue as with using a PartialEq key in a HashMap, but for some reason it is considered appropriate here.

What is an example of a reasonable function which requires only PartialEq and adding Eq is not desirable/does not make sense?

If there are no such use cases, then why do we care about splitting it into two traits PartialEq / Eq? Haskell, for example, just has Eq.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
dying_sphynx
  • 1,136
  • 8
  • 17
  • 3
    You are greatly dis-servicing yourself by thinking of floating point at all, where equality is almost never the right operation to start with. There are other types. – Shepmaster Mar 12 '19 at 19:07
  • 1
    *"I may be able to understand it better if I find an example of code which does not require Eq."* -- I think you've placed an unnecessary qualification on your question here. Why not just have it explained to you exactly why `HashMap` requires `Eq`? You've assumed *"We have the same basic issue"* in the case of `assert_eq!`, but this is an unwarranted assumption (because you've already admitted you don't know what this issue is). – Benjamin Lindley Mar 12 '19 at 19:17
  • 1
    @Shepmaster well, equality on floats is a nice and simple example of a relation which is not reflexive but well known to almost everybody... It's even present here: https://en.wikipedia.org/wiki/Partial_equivalence_relation – dying_sphynx Mar 12 '19 at 19:17
  • @BenjaminLindley hm, but I understand why `HashMap` requires `Eq`: so that it is able to find all the keys. Having `Eq` means that we don't have values `x` such that `x != x`. It makes sense for lookups. But what are the examples when we don't need `Eq` and are content only for `PartialEq`? Reflexivity is a nice property, why abandon it and just use `PartialEq`? And if we always want `Eq` then why have `PartialEq` trait at all? – dying_sphynx Mar 12 '19 at 19:20
  • @dying_sphynx: But you already named an example (`assert_eq!`), that only requires `PartialEq`. – Benjamin Lindley Mar 12 '19 at 19:24
  • @BenjaminLindley Indeed, but what is the property of a function which allows us to decide whether to require `Eq` or `PartialEq`? Is it that we can use `PartialEq` if we never intend to compare `x` to `x` and thus don't care about reflexivity? – dying_sphynx Mar 12 '19 at 19:27
  • @dying_sphynx: You could, for example, have some kind of list you are searching where all non-comparable values have already been filtered out. – Benjamin Lindley Mar 12 '19 at 19:38
  • @BenjaminLindley I don't fully understand what you mean here... I thought that `PartialEq` is not about "non-comparable items" (non-comparable to what?), but that it means that we can have values of the type that are not equal to themselves (even all of them can be like this). – dying_sphynx Mar 12 '19 at 19:43
  • 1
    @SpencerPark This is a very lucid explanation packed in a small number of words, kudos. The explanation [in the book](https://doc.rust-lang.org/book/appendix-03-derivable-traits.html#partialeq-and-eq-for-equality-comparisons) technically says the same thing, but it's not as clear. It should really be an answer. – user4815162342 Mar 12 '19 at 20:03
  • The gist I was looking for: yes, `assert_eq!(f64::NAN, some_code_producing_nan())` is silly, but that doesn't mean you assert_eq should require Eq, because then you couldn't use it with floats other than NAN (like `assert_eq!(cached_value, recalculate_value(x))`), nor on other PartialEq-only types. I guess that ideally the compiler would warn if an argument of assert_eq has a known value that isn't even equal to itself. – Stein Mar 15 '19 at 18:10

1 Answers1

29

Deciding when to use PartialEq vs Eq should be based on whether the use requires that x == x.

The question is not about whether it is possible to compare x to x but rather if that comparison happens, does the use depend on x==x always holding? If the answer is yes, use Eq. Otherwise prefer the weaker constraint PartialEq.

assert_eq!doesn't depend on x==x always holding so there is no need to force that constraint on the caller. As OP succinctly mentioned 2 examples in the comments:

if we do assert_eq!(NAN, produces_nan()) - it's our problem that it gives false, but if we do a lookup of a NAN key in a HashMap, it would be a problem of the HashMap, because it would violate its lookup contract (that it should be able to find all the keys put in the map)

SpencerPark
  • 3,298
  • 1
  • 15
  • 27
  • 1
    I was thinking more about that trying: 1) to come up with more examples of non-reflexive types (except floats and structures containing them) and 2) to come up with any other useful functions requiring only PartialEq as opposed to Eq. I couldn't find any, so it's starting to look to me that all this business of PartialEq vs Eq separation has been caused by floats and NaN and desire to control in types such potentially wrong things as sorting vector of floats (which will work "randomly" in presents of NaN). – dying_sphynx Mar 16 '19 at 11:52