Please consider this as an addendum to the original answers, which are suited for your specific case. This answer deals with the second part of your question.
Consider this struct:
struct Person {
id: u32,
name: String,
height: u32,
}
Equality: the PartialEq
and Eq
traits
PartialEq Trait, From the docs
Trait for equality comparisons which are partial equivalence
relations. This trait allows for partial equality, for types that do not
have a full equivalence relation. For example, in floating point numbers
NaN != NaN, so floating point types implement PartialEq but not Eq.
Formally, the equality must be (for all a, b and c):
symmetric: a == b implies b == a; and
transitive: a == b and b == c implies a == c.
So if you want to express what it means for values of your types to be equal, you must implement the PartialEq
trait. Implementing it allows us to write x == y
and x != y
for our types.
impl PartialEq for Person {
fn eq(&self, other: &Person) -> bool {
self.height == other.height
}
}
Note that we are deciding the equality of Person
struct just on the basis of the heights. You could also have implemented this eq
method if you want to compare every struct field:
fn eq(&self, other: &Person) -> bool {
self.id == other.id && self.name == other.name && self.height == other.height
}
But it would be easier to simply add #[derive(PartialEq)]
if that's the behavior you want.
Eq Trait, From the Docs
Trait for equality comparisons which are equivalence relations.
This means, that in addition to a == b and a != b being strict inverses,
the equality must be (for all a, b and c):
reflexive: a == a;
symmetric: a == b implies b == a; and
transitive: a == b and b == c implies a == c.
This property cannot be checked by the compiler, and therefore Eq implies
PartialEq, and has no extra methods.
Derivable
This trait can be used with #[derive]. When derived, because Eq has no extra methods,
it is only informing the compiler that this is an equivalence relation rather than a
partial equivalence relation. Note that the derive strategy requires all
fields are Eq, which isn't always desired.
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).
You can also implement the Eq
trait manually with an empty impl block
impl Eq for Person {}
But, again, it’s easier to add Eq
to your #[derive(Eq)]
list.
Ordering: the PartialOrd
and Ord
traits
The relative ordering of values is calculated using the operators <
, <=
, >=
and >
. To implement these for your own types, you must implement the PartialOrd
trait.
Before you can implement PartialOrd
, you must implement PartialEq
.
impl PartialOrd for Person {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
Ordering
is an enum with these values:
pub enum Ordering {
Less,
Equal,
Greater,
}
partial_cmp
returns an Option
and not an Ordering
because there are types of which values cannot always be compared, such as floating-point numbers. NaN
s are not representable numbers; expressions such as 3.0 < NaN
don’t make any sense. In those cases, partial_cmp
returns None
. Floating-point values are the only case in the standard library where this happens. More can be found here.
The fact that partial_cmp
returns an Option<Ordering>
has a consequence: it might not be possible to place two values, x and y, into a definite order. In practice, this means that implementing PartialOrd
is not sufficient to make your values sortable. You also need to implement the Ord
trait.
Before you can implement Ord
, you must first implement PartialOrd
, Eq
and PartialEq
.
For our Person
struct, again we can delegate down to one of our member variables:
impl Ord for Person {
fn cmp(&self, other: &Person) -> Ordering {
self.height.cmp(&other.height)
}
}
PartialOrd
for everything withOrd
", OP doesn't seem to want to requireOrd
for types withPartialOrd
, just having a default implementation for types implementingOrd
to reduce redundancy. That is entirely reasonable and sound, it's just not possible in the trait system as of now (but probably doesn't bring enough benefit to be included in the future). – Wrought