This is similar to How do I use a custom comparator function with BTreeSet? however in my case I won't know the sorting criteria until runtime. The possible criteria are extensive and can't be hard-coded (think something like sort by distance to target or sort by specific bytes in a payload or combination thereof). The sorting criteria won't change after the map/set is created.
The only alternatives I see are:
Vec
, but log(n) inserts and deletes are crucialThis is possible with standard C++ containers std::map
/std::set
but doesn't seem possible with Rust's BTreeMap
/BTreeSet
. Is there an alternative in the standard library or in another crate that can do this? Or will I have to implement this myself?
My use-case is a database-like system where elements in the set are defined by a schema, like:
Element {
FIELD x: f32
FIELD y: f32
FIELD z: i64
ORDERBY z
}
But since the schema is user-defined at runtime, the elements are stored in a set of bytes (BTreeSet<Vec<u8>>
). Likewise the order of the elements is user-defined. So the comparator I would give to BTreeSet
would look like |a, b| schema.cmp(a, b)
. Hard-coded, the above example may look something like:
fn cmp(a: &Vec<u8>, b: &Vec<u8>) -> Ordering {
let a_field = self.get_field(a, 2).as_i64();
let b_field = self.get_field(b, 2).as_i64();
a_field.cmp(b_field)
}
There is the copse1 crate written by @eggyal which provides BTreeSet
, BTreeMap
, and BinaryHeap
types mimicking the standard library but which allow a ordering to be specified separately.
Here is an example BTreeSet
of Point
s that are ordered by a distance to a fixed point:
use copse::{BTreeSet, SortableBy, TotalOrder};
struct Point {
x: f64,
y: f64,
}
impl Point {
fn distance(&self, other: &Point) -> f64 {
let dx = self.x - other.x;
let dy = self.y - other.y;
(dx * dx + dy * dy).sqrt()
}
}
struct PointTotalOrder(Point);
impl TotalOrder for PointTotalOrder {
type OrderedType = Point;
fn cmp(&self, this: &Point, that: &Point) -> std::cmp::Ordering {
let this_dist = self.0.distance(this);
let that_dist = self.0.distance(that);
f64::total_cmp(&this_dist, &that_dist)
}
}
impl SortableBy<PointTotalOrder> for Point {
fn sort_key(&self) -> &Point {
self
}
}
fn main() {
let fixed = Point { x: 2.0, y: 3.0 };
let mut set = BTreeSet::new(PointTotalOrder(fixed));
set.insert(Point { x: 0.0, y: 0.0 });
set.insert(Point { x: 3.0, y: 3.0 });
set.insert(Point { x: 2.0, y: 5.0 });
for point in set {
println!("({}, {})", point.x, point.y);
}
}
(3, 3)
(2, 5)
(0, 0)
1. "Copse" is a word meaning "a small group of trees" which I find very fitting.