Option.max and min

No... but nonetheless, a decision had to be made for the sake of generic code.

In my honest view, I do not always consider None as the "absence" of a value. Sometimes I do; and other times, I consider it to be just another value (where Option is used to extend a type with a single additional value).


This problem doesn't just affect Option. Many types that have normally have no business at all being compared are given PartialOrd and Ord implementations simply for the sake of supporting algorithms. A crate like num-complex has to make a tough decision:

  • if Complex does not derive Ord, you won't be able to do cool things like construct a BTreeMap whose keys are gaussian integers.
  • If Complex does derive Ord, everybody's code will be full of heinous bugs where they accidentally used the < operator on complex numbers.

All things considered, I wish Rust did not conflate the ordered comparisons we care about (i.e. the places where we actually want to use < and max) together with the ordered comparisons that exist because they can (i.e. the kind that are used by BTreeMap and algorithms). I imagine an alternate universe with three traits:

// * can use #[derive(Lex)]
// * used in bounds for BTreeMap
// * there might also be a Vec::lex_sort
/// Trait for lexical comparisons.
trait Lex {
    fn lex_cmp(&self, other: &Self) -> Ordering;
}

// * cannot be derived (it now serves a role like Add and Sub)
/// Trait for the `<`, `>`, and etc. operators.
trait PartialOrd {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering>;
}

// * cannot be derived
// * used by Vec::sort, Iterator::min, Iterator::max
trait Ord: PartialOrd + Eq {
    fn cmp(&self, other: &Self) -> Ordering { ... }
    fn min(&self, other: &Self) -> &Self { ... }
    fn max(&self, other: &Self) -> &Self { ... }
}
3 Likes