19

In C++, it is possible to customize the code std::set uses to sort its arguments. By default it uses std::less, but that can be changed with the Compare template parameter.

Rust's BTreeSet uses the Ord trait to sort the type. I don't know of a way to override this behavior -- it's built into the type constraint of the type stored by the container.

However, it often makes sense to build a list of items that are sorted by some locally-useful metric that nevertheless is not the best way to always compare the items by. Or, suppose I would like to sort items of a used type; in this case, it's impossible to implement Ord myself for the type, even if I want to.

The workaround is of course to build a plain old Vec of the items and sort it afterward. But in my opinion, this is not as clean as automatically ordering them on insertion.

Is there a way to use alternative comparators with Rust's container types?

George Hilliard
  • 15,402
  • 9
  • 58
  • 96
  • 2
    A nicer (or at least different) workaround is to define a newtype around the element type and implement your desired `Ord` instance on that. – fjh Dec 01 '15 at 20:03
  • True, although then you have your code littered with `map`s to and from the custom type (which of course compile away to nothing). Still, decent idea. I wonder if it would be possible to create a wrapper type for that pattern, where you pass the custom type constructor the source type and the comparator. – George Hilliard Dec 01 '15 at 20:11
  • @thirtythreeforty, what do you mean by an O(log n) operation becoming O(n log n)? (I can't see an operation that is O(log n) other than, say, a single tree operation, but nothing that involves building a list will be O(log n).) – huon Dec 02 '15 at 06:01
  • @huon oh, that's true. Duh. Well that makes me feel a little better about the way I'm doing it now. – George Hilliard Dec 02 '15 at 06:02
  • This may be far too late to be of any use to you @GeorgeHilliard, but for future readers of this question it may be helpful to know that my (recently published) [copse](https://crates.io/crates/copse) crate provides an alternative `BTreeSet` that can be instantiated with a custom/runtime-defined comparator. – eggyal Jan 16 '23 at 19:25

1 Answers1

16

Custom comparators currently do not exist in the Rust standard collections. The idiomatic way to solve the issue is to define a newtype:

struct Wrapper(Wrapped);

You can then define a custom Ord implementation for Wrapper with exactly the semantics you want.

Furthermore, since you have a newtype, you can also easily implement other traits to facilitate conversion:

Note that accessing the wrapped entity is syntactically lightweight as it's just two characters: .0.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 2
    Great! Now that you mention the various traits, this might even end up being cleaner from a type perspective than C++'s approach. – George Hilliard Dec 02 '15 at 16:06