PreRFC: std::cmp::CmpKey
trait
Motivation
Currently, there are four traits in the std::cmp
crate about the comparing:
std::cmp::PartialEq
std::cmp::Eq
std::cmp::PartialOrd
std::cmp::Ord
Users must ensure every methods implementation are consistent, otherwise some terrible things may happen.
The constraints includes:
- methods in the same trait:
eq
andne
,cmp
andgt
- methods in different traits:
eq
,partial_eq
andpartial_cmp
In most cases, we only need some fields projection in the implementation, but sometimes we may make some foolish bugs and introduce terrible results. The bug is hard to reported by clippy.
struct Row {
x: i32,
y: i32,
// Assume there were some complex fields which disable we using derive here.
_z: (),
}
impl PartialEq for Row {
fn eq(&self, other: &Self) -> bool {
// A stupid bug
self.x == other.x && self.y == other.x
}
}
We may use Row
in BTreeMap
or other collections and it may be hard to find the bug.
So how about introducing a new trait which is simple enough for users to implement?
Proposed solution
trait CmpKey {
type K<'a>
where
Self: 'a;
fn key(&self) -> Self::K<'_>;
}
Only one method need to be implemented!
impl CmpKey for Row {
type K<'a> = (i32, i32);
fn key(&self) -> Self::K<'_> {
(self.x, self.y)
}
}
GAT here is necessary for large type:
struct Row2 {
x: String,
y: i32,
_z: (),
}
impl CmpKey for Row2 {
type K<'a> = (&'a str, i32);
fn key(&self) -> Self::K<'_> {
(self.x.as_ref(), self.y)
}
}
We can automatically implement all the four traits if users implement CmpKey
.
impl<C> PartialEq for C
where
C: CmpKey,
for<'a> C::K<'a>: PartialEq<C::K<'a>>,
{
fn eq(&self, other: &Self) -> bool {
self.key().eq(&other.key())
}
fn ne(&self, other: &Self) -> bool {
self.key().ne(&other.key())
}
}
impl<C> Eq for C
where
C: CmpKey,
for<'a> C::K<'a>: Eq,
{}
impl<C> PartialOrd for C
where
C: CmpKey,
for<'a> C::K<'a>: PartialOrd<C::K<'a>>
{
...
}
impl<C> Ord for C
where
C: CmpKey,
for<'a> C::K<'a>: Ord
{
...
}
The trait can only be added in std lib due to the orphan rule.