Implementing Ord for a type is awkward?

岁酱吖の 提交于 2019-12-30 08:10:37

问题


I have a newtype and I want to implement Ord:

use std::cmp::{Ord, Ordering};

struct MyType(isize);

impl Ord for MyType {
    fn cmp(&self, &other: Self) -> Ordering {
        let MyType(ref lhs) = *self;
        let MyType(ref rhs) = *other;
        lhs.cmp(rhs)
    }
}

When I try to compare two variables of my types, I get errors:

error[E0277]: the trait bound `MyType: std::cmp::PartialOrd` is not satisfied
 --> src/main.rs:5:6
  |
5 | impl Ord for MyType {
  |      ^^^ can't compare `MyType` with `MyType`
  |
  = help: the trait `std::cmp::PartialOrd` is not implemented for `MyType`

error[E0277]: the trait bound `MyType: std::cmp::Eq` is not satisfied
 --> src/main.rs:5:6
  |
5 | impl Ord for MyType {
  |      ^^^ the trait `std::cmp::Eq` is not implemented for `MyType`

When I implement PartialEq, Eq and PartialOrd (gt(), lt(), eq(), ge(), le(), etc.), everything works fine, but if I provided cmp, we can infer functions like lt() and eq()! This is redundant! I don't like this!

When looking in the docs, I see this in the definition of Ord:

pub trait Ord: Eq + PartialOrd<Self> 

This looks like the trait inherits from Eq and PartialOrd. Why can't the trait provide default implementations for the required methods from the inherited traits using the cmp function? I don't know how inheritance of traits works and searching turned up nothing useful, but I think this is something that should be possible.

How is this done in Rust? I hope not this way...


回答1:


For your specific case I'd use #[derive]:

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
struct MyType(isize);

fn main() {
    let a = MyType(5);
    let b = MyType(6);

    println!("{:?}", a.cmp(&b))
}

If you need to special case your Ord implementation, you still have to write out that code, there's nothing that can save us there! You can still derive implementations for the other traits, if it makes sense to do so.

The other part of your question is ambiguous, so I'll answer it both ways I read it:

Why can't Ord be automatically provided by anything with PartialOrd?

Let's look at the docs for PartialOrd and Ord.

PartialOrd says: "The comparison must satisfy antisymmetry and transitivity", while Ord says: "types that form a total order". These are math terms and I won't do as good a job as Wikipedia will at describing them.

However, we can use floating point numbers as an example. Floats have a special value called NaN. Comparing against this value is tricky. For example, all of 1.0 < NaN, 1.0 == NaN and 1.0 > NaN are false! These don't form a total order, but we can still compare one value against another. This is why PartialOrd exists - to allow us to compare types like this. incidentally, PartialEq exists for similar reasons.

We can't define Ord in terms of PartialOrd because we don't have the appropriate guarantees about the underlying types. This is Rust's type system saving us from making a mistake!

Why can't PartialOrd be automatically provided by anything with Ord?

The problem here is that more types are PartialOrd than they are Ord. If we required everything to be Ord, then we couldn't have any floating point comparisons, because they don't have a total order, or we'd have to give up on having a total order and losing out on the safety that it provides.

However, there have been ideas to automatically do this for derive. Proposed RFC 2385 would allow the above code to be reduced to:

#[derive(Debug, Copy, Eq, Ord)]
struct MyType(isize);

when I implement PartialOrd (gt(), lt(), eq(), ge(), le() ...)

Note that PartialOrd does have default implementations, you only need to implement cmp. The others are there for ease-of-use or possibly performance reasons.




回答2:


For starters, you can implement only PartialOrd::partial_cmp, as lt, le, gt, and ge have default implementations. Furthermore, if you implement Ord, you can simply implement cmp as usual and partial_cmp becomes just Some(self.cmp(other)).

However, if you only want to delegate to some field's notion of equality and ordering, then it's far better and easier to derive:

#[derive(PartialOrd, Ord, PartialEq, Eq)]
struct MyType(isize);


来源:https://stackoverflow.com/questions/28387711/implementing-ord-for-a-type-is-awkward

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!