当前位置: 首页 > 知识库问答 >
问题:

为类型实现Ord很尴尬?

丁毅庵
2023-03-14

我有一个新类型,我想实现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)
    }
}

当我尝试比较我的类型的两个变量时,会出现错误:

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`

当我实现PartialEqEqPartialOrdgt()lt()Eq()ge()le())时,一切都很好,但如果我提供cmp,我们可以推断出lt()Eq()之类的函数!这是多余的!我不喜欢这个!

当查看文档时,我在Ord的定义中看到了这一点:

pub trait Ord: Eq + PartialOrd<Self> 

这看起来像是从EqPartialOrd继承的特征。为什么trait不能使用cmp函数为继承的trait提供所需方法的默认实现?我不知道遗传特性是如何起作用的,搜索也没有什么用处,但我认为这应该是可能的。

这是如何在铁锈中完成的?我希望不是这样。。。

共有3个答案

汪典
2023-03-14

请将此视为原始答案的补充,适合您的具体情况。这个答案涉及你问题的第二部分。

考虑这个结构:

struct Person {
    id: u32,
    name: String,
    height: u32,
}

PartialEq特性,来自文档

Trait for equality comparisons which are partial equivalence 
relations. This trait allows for partial equality, for types that do not
have a full equivalence relation. For example, in floating point numbers
NaN != NaN, so floating point types implement PartialEq but not Eq.

Formally, the equality must be (for all a, b and c):

symmetric: a == b implies b == a; and
transitive: a == b and b == c implies a == c.

因此,如果您想表达类型的值相等意味着什么,您必须实现ParatalEq特性。实现它允许我们写x==yx!=y为我们的类型。

impl PartialEq for Person {
    fn eq(&self, other: &Person) -> bool {
        self.height == other.height
    }
}

请注意,我们只是根据高度来确定Personstruct的相等性。如果要比较每个结构字段,也可以实现此eq方法:

fn eq(&self, other: &Person) -> bool {
     self.id == other.id && self.name == other.name && self.height == other.height
}

但是,如果您想要的是这种行为,那么简单地添加#[派生(部分Eq)]会更容易。

Eq特征,来自文档

Trait for equality comparisons which are equivalence relations.

This means, that in addition to a == b and a != b being strict inverses, 
the equality must be (for all a, b and c):

reflexive: a == a;
symmetric: a == b implies b == a; and
transitive: a == b and b == c implies a == c.

This property cannot be checked by the compiler, and therefore Eq implies
PartialEq, and has no extra methods.

Derivable
This trait can be used with #[derive]. When derived, because Eq has no extra methods, 
it is only informing the compiler that this is an equivalence relation rather than a 
partial equivalence relation. Note that the derive strategy requires all 
fields are Eq, which isn't always desired.

PartialEq用于不一定是自反的关系(也就是说,可以有这样的x,x!=x),Eq是一个标记特征,表示关系也是自反的(现在它是一个适当的等价关系)。

您还可以使用空impl块手动实现Eqtrait

impl Eq for Person {}

但是,同样,将Eq添加到#[派生(Eq)]列表更容易。

使用运算符

在实现PartialOrd之前,必须先实现PartialEq

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

排序是具有以下值的枚举:

pub enum Ordering {
    Less,
    Equal,
    Greater,
}

partial\u cmp返回一个选项,而不是一个排序,因为有些类型的值不能总是进行比较,例如浮点数NaNs不是可表示的数字;表达式,如3.0

事实上,partial_cmp返回一个选项

在实现Ord之前,必须先实现PartialOrdEqPartialEq

对于我们的Personstruct,我们可以再次委托给我们的一个成员变量:

impl Ord for Person {
    fn cmp(&self, other: &Person) -> Ordering {
        self.height.cmp(&other.height)
    }
}

拓拔俊德
2023-03-14

对于初学者,您只能实现PartialOrd::partial_cmp,因为ltlegt、和ge都有默认实现。此外,如果您实现Ord,您可以像往常一样简单地实现cmp,而partial\u cmp只会变成Some(self.cmp(other))

然而,如果您只想委托给某个字段的平等和有序概念,那么它要好得多,也更容易推导:

#[derive(PartialOrd, Ord, PartialEq, Eq)]
struct MyType(isize);
柴磊
2023-03-14

对于您的具体情况,我将使用#[派生]

#[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))
}

如果您需要对您的Ord实现进行特殊处理,您仍然需要写出代码,没有什么可以帮我们解决这个问题!如果这样做有意义的话,您仍然可以派生其他特性的实现。

你问题的另一部分模棱两可,因此我将从两个方面回答:

为什么使用PartialOrd的任何东西都不能自动提供Ord

让我们看看PartialOrdOrd的文档。

PartialOrd说:“比较必须满足反对称性和及物性”,而Ord说:“形成总顺序的类型”。这些都是数学术语,我不会像维基百科那样描述它们。

但是,我们可以使用浮点数作为示例。浮点数有一个特殊的值,称为NaN。与这个值进行比较是很棘手的。例如,所有的1.0

我们不能用PartialOrd定义Ord,因为我们对底层类型没有适当的保证。这是Rust的类型系统,可以避免我们犯错误!

为什么使用Ord的任何东西都不能自动提供PartialOrd

这里的问题是PartialOrd类型比Ord类型多。如果我们要求所有内容都是Ord,那么我们就不能进行任何浮点比较,因为它们没有总顺序,否则我们就不得不放弃总顺序,失去它提供的安全性。

但是,有一些想法可以自动为派生执行此操作。拟议的RFC 2385将允许将上述代码缩减为:

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

当我实现PartialOrdgt()lt()eq()ge()le()

请注意,PartialOrd有默认实现,您只需要实现partial\u cmp。其他的是为了便于使用或可能是性能原因。

 类似资料:
  • 在的留档中,它说 实现必须与PartialOrd实现一致[…] 这当然是有道理的,并且可以很容易地归档,如下面的示例所示: 我想知道,为什么他们会把这个负担/风险留给我们用户,而不是用毯子 我在操场上测试了循环依赖和其他东西的问题,但这和我预期的一样有效。互联网也没有产生任何结果。 我能想到的另一个原因是宏现在是如何工作的。人们可能必须将每个替换为(或者将(PartialOrd)的宏变得更智能-我

  • 我见过一个类似的问题,但是没有人告诉我如何为结构实现Ord。例如,以下内容: 这给了我一个错误: 我该怎么解决这个问题?我尝试将实现更改为: 并添加适当的和函数,但这给了我一个错误,即这两种方法都不是的成员。

  • 我正在尝试将与自定义结构一起使用。为此,我必须让我的结构实现trait,但我需要的是相同结构的2个s,但顺序不同。 有没有办法定义2 Ord实现 我想我可以定义两个不同的包装结构,它们保留了对原始自定义结构的引用,并为每一个定义了一个的实现,但是必须构建大量这样的包装结构的实例似乎是相当浪费的。 在Pyhton/Java我会提供一个排序函数/比较器,但似乎没有这样的设施。在Scala中,我可以定义

  • 本文向大家介绍awk中实现ord函数功能,包括了awk中实现ord函数功能的使用技巧和注意事项,需要的朋友参考一下 在awk中并未直接提供ord函数,所以在将某个字符转换为码时,需要自己来实现 这里主要是构造了ord为key,val的数据结构,key为char字符,val对应的ascii码 附:awk得到字母的ASC值实现

  • ord

    描述 (Description) 此函数返回EXPR指定的字符的ASCII数值,如果省略则返回$ _。 例如,ord('A')返回值65。 语法 (Syntax) 以下是此函数的简单语法 - ord EXPR ord 返回值 (Return Value) 此函数返回Integer。 例子 (Example) 以下是显示其基本用法的示例代码 - #!/usr/bin/perl -w print("

  • 我想要一个方法,用于、和,因此我创建了一个trait: 我得到一个错误: 根据Rust标准库文档,未实现。为什么存在冲突的实现?