I need an ordered set so I'm using BTreeSet. I have an Item struct which contains a unique id, and a rank field which may be duplicated. I only want uniqueness to be considered for the id field and for it to be sorted on the rank field, so I've manually implemented PartialEq, PartialOrd, and Ord to do this, but it's not working as I expected
Here's the code:
use std::cmp::Ordering;
use std::collections::BTreeSet;
#[allow(dead_code)]
fn main() {
let mut set = BTreeSet::new();
set.insert(Item {
id: 0,
rank: 0,
});
set.insert(Item {
id: 1,
rank: 1,
});
set.insert(Item {
id: 2,
rank: 1,
});
for item in set.iter() {
println!("{:?}", item);
}
}
#[derive(Debug, Eq)]
struct Item {
id: u32,
rank: u32,
}
impl PartialEq for Item {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl PartialOrd for Item {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Item {
fn cmp(&self, other: &Self) -> Ordering {
self.rank.cmp(&other.rank)
}
}
I get the output
Item { id: 0, rank: 0 }
Item { id: 1, rank: 1 }
When I expected
Item { id: 0, rank: 0 }
Item { id: 1, rank: 1 }
Item { id: 2, rank: 1 }
Why is this happening and how can I fix this?
This happens because you violated multiple of the contracts of PartialOrd
:
a == b
if and only ifpartial_cmp(a, b) == Some(Equal)
and
If
Ord
is also implemented forSelf
andRhs
, it must also be consistent withpartial_cmp
But BTreeSet
relies on those to hold.
You can't implement your desired behavior with BTreeSet
alone and should use a custom container instead.