如何为一个结构体实现Ord接口?

43

我看过一个类似的问题,但没有人能告诉我如何为结构体实现Ord。例如,以下内容:

struct SomeNum {
    name: String,
    value: u32,
}

impl Ord for SomeNum {
    fn cmp(&self, other:&Self) -> Ordering {
        let size1 = self.value;
        let size2 = other.value;
        if size1 > size2 {
            Ordering::Less
        }
        if size1 < size2 {
            Ordering::Greater
        }
        Ordering::Equal
    }
}

这让我产生了错误:

error: the trait `core::cmp::Eq` is not implemented for the type `SomeNum` [E0277]

我该如何修复这个问题?我已经尝试将实现更改为:

impl Ord for SomeNum where SomeNum: PartialOrd + PartialEq + Eq {...}

我尝试添加适当的partial_cmpeq函数,但是编译器提示这两个方法都不是Ord的成员。

并添加适当的partial_cmpeq函数,但它告诉我这两个方法都不是Ord的成员。


6
我曾看到一个类似的问题,但请 在您找到这些问题后包含链接。这使得回答者可以更好地了解您已经阅读了哪些解释并且不理解,否则我们有可能只是重复您已经知道的东西,浪费大家的时间! 这也对于未来的搜索者很有帮助,因为他们可以轻松地查看相关问题。 - Shepmaster
1个回答

64

Ord的定义如下:

pub trait Ord: Eq + PartialOrd<Self> {
    fn cmp(&self, other: &Self) -> Ordering;
}

任何实现 Ord 的类型也必须实现 EqPartialOrd<Self>。你必须为 SomeNum 实现这些 trait。

顺便说一下,你的实现看起来是反过来的;如果你只比较 self.valueself.value > other.value 应该是 Greater,而不是 Less

如果需要,你可以使用 u32 上的 Ord 实现来协助: self.value.cmp(other.value)

你还应该考虑到,Ord 是一个 完全排序。例如,如果你的 PartialEq 实现考虑了 name,那么你的 Ord 实现也必须考虑。最好使用元组来方便表示(表明比较中最重要的字段是 value,但如果它们相同,则应考虑 name),类似于这样:

struct SomeNum {
    name: String,
    value: u32,
}

impl Ord for SomeNum {
    fn cmp(&self, other: &Self) -> Ordering {
        (self.value, &self.name).cmp(&(other.value, &other.name))
    }
}

impl PartialOrd for SomeNum {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for SomeNum {
    fn eq(&self, other: &Self) -> bool {
        (self.value, &self.name) == (other.value, &other.name)
    }
}

impl Eq for SomeNum { }

如果你这样做的话,不如重新排序字段并使用#[derive]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct SomeNum {
    value: u32,
    name: String,
}

这将扩展为基本相同的内容。


谢谢!还有一件事。如果我想手动计算 cmp,我应该返回什么来表示更大、更小或相等。我尝试过上述方法,并在适当的时候返回 Ordering::Less、Ordering::Greater 和 Ordering::Equal,但编译器指出 cmp 期望输出类型为 () 而不是 core::cmp::Ordering。这只发生在 if 语句中出现 Less 和 Greater 情况的情况下。 - Dumbapples
3
是的,你需要的是 if size1 < size2 { Ordering::Less } else if size1 > size2 { Ordering::Greater } else { Ordering::Equal }。一个 if 表达式有一个类型,你实际上有三个语句连续执行,前两个语句的值没有被使用,并且类型不一致,因为它们有一个隐含的 else { },它的类型是 () - Chris Morgan
1
OrdPartialEq 派生 PartialOrdEq 吗?只需将它们各自的返回值包装在 Some 中即可吗? - BallpointBen
@BallpointBen:嗯,Eq只是一个标记,表示PartialEq实现实际上是完全的;对于PartialOrd,请参见https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html#how-can-i-implement-partialord。但这不是*派生*,你需要自己编写实现,尽管它很简单。 - Chris Morgan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接