为类型实现Ord很尴尬?

Kap*_*chu 10 traits rust ord

我有一个newtype,我想实现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)
    }
}
Run Code Online (Sandbox Code Playgroud)

当我尝试比较我的类型的两个变量时,我得到错误:

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`
Run Code Online (Sandbox Code Playgroud)

当我执行PartialEq,EqPartialOrd(gt(),lt(),eq(),ge(),le(),等),一切工作正常,但如果我提供的cmp,我们可以推断出类似的功能lt()eq()!这是多余的!我不喜欢这个!

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

pub trait Ord: Eq + PartialOrd<Self> 
Run Code Online (Sandbox Code Playgroud)

这看起来像trait继承自EqPartialOrd.为什么特征不能使用该cmp函数从继承的特征中为所需方法提供默认实现?我不知道traits的继承是如何起作用的,而且搜索没有任何用处,但我认为这是应该可行的.

怎么在Rust中完成?我希望不是这样的......

She*_*ter 10

对于您的具体情况,我会使用#[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))
}
Run Code Online (Sandbox Code Playgroud)

如果你需要特殊情况你的Ord实现,你仍然必须写出代码,没有什么可以拯救我们!如果有意义的话,您仍然可以派生其他特征的实现.

你问题的另一部分含糊不清,所以我会以两种方式回答它:

为什么不能通过Ord任何东西自动提供PartialOrd

让我们来看看文档的PartialOrdOrd.

PartialOrd他说:"比较必须满足反对称性传递性 ",同时Ord说:"形成总秩序的类型".这些都是数学术语,我不会像维基百科那样描述它们.

但是,我们可以使用浮点数作为示例.浮点数具有特殊值NaN.与这个值相比是很棘手的.例如,所有的1.0 < NaN,1.0 == NaN并且1.0 > NaN都是假的!这些不构成总订单,但我们仍然可以将一个值与另一个值进行比较.这就是PartialOrd存在的原因- 允许我们比较这样的类型.顺便说一下,PartialEq出于类似的原因存在.

我们无法定义Ord,PartialOrd因为我们没有对基础类型的适当保证.这是Rust的类型系统,使我们免于犯错误!

为什么不能通过PartialOrd任何东西自动提供Ord

这里的问题是PartialOrd比他们更多的类型Ord.如果我们需要一切Ord,那么我们就不能进行任何浮点比较,因为它们没有总订单,或者我们不得不放弃总订单而失去它提供的安全性.

但是,有一些想法可以自动执行此操作derive.建议的RFC 2385允许将上述代码简化为:

#[derive(Debug, Copy, Eq, Ord)]
struct MyType(isize);
Run Code Online (Sandbox Code Playgroud)

当我执行PartialOrd(gt(),lt(),eq(),ge(),le()...)

请注意,PartialOrd 确实有默认实现,您只需要实现cmp.其他的是易于使用或可能的性能原因.

  • 关于``PartialOrd`用于'Ord`的所有东西',OP似乎不想要'Ord`用于带有'PartialOrd`的类型,只是对实现`Ord`的类型有*default*实现来减少冗余.这是完全合理和合理的,它在现在的特质系统中是不可能的(但可能没有带来足够的好处被包括在未来). (2认同)
  • 我认为你没有很好地回答“为什么任何带有‘Ord’的东西不能自动提供‘PartialOrd’?”的问题。这个问题要求自动`PartialOrd` **仅**当已经假定`Ord`时,这并不总是,如果不总是这样,那么“如果我们要求一切都是`Ord`”从何而来?听起来相当混乱。 (2认同)

Gra*_*Dot 10

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

\n\n

考虑这个结构:

\n\n
struct Person {\n    id: u32,\n    name: String,\n    height: u32,\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

平等:PartialEqEq特征

\n\n

PartialEq Trait,来自文档

\n\n
Trait for equality comparisons which are partial equivalence \nrelations. This trait allows for partial equality, for types that do not\nhave a full equivalence relation. For example, in floating point numbers\nNaN != NaN, so floating point types implement PartialEq but not Eq.\n\nFormally, the equality must be (for all a, b and c):\n\nsymmetric: a == b implies b == a; and\ntransitive: a == b and b == c implies a == c.\n
Run Code Online (Sandbox Code Playgroud)\n\n

因此,如果您想表达类型值相等的含义,则必须实现该PartialEq特征。实现它允许我们为我们的类型编写x == yx != y

\n\n
impl PartialEq for Person {\n    fn eq(&self, other: &Person) -> bool {\n        self.height == other.height\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
fn eq(&self, other: &Person) -> bool {\n     self.id == other.id && self.name == other.name && self.height == other.height\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

#[derive(PartialEq)]但如果这是您想要的行为,那么简单地添加会更容易。

\n\n

Eq Trait,来自文档

\n\n
Trait for equality comparisons which are equivalence relations.\n\nThis means, that in addition to a == b and a != b being strict inverses, \nthe equality must be (for all a, b and c):\n\nreflexive: a == a;\nsymmetric: a == b implies b == a; and\ntransitive: a == b and b == c implies a == c.\n\nThis property cannot be checked by the compiler, and therefore Eq implies\nPartialEq, and has no extra methods.\n\nDerivable\nThis trait can be used with #[derive]. When derived, because Eq has no extra methods, \nit is only informing the compiler that this is an equivalence relation rather than a \npartial equivalence relation. Note that the derive strategy requires all \nfields are Eq, which isn\'t always desired.\n
Run Code Online (Sandbox Code Playgroud)\n\n

PartialEq 适用于不一定是自反的关系(即可以存在 x != x 的 x),并且 Eq 是一个标记特征,表明该关系也是自反的(现在它是一个正确的等价关系)。

\n\n

您还可以Eq使用空的 impl 块手动实现该特征

\n\n
impl Eq for Person {}\n
Run Code Online (Sandbox Code Playgroud)\n\n

Eq但是,再次强调,将其添加到您的列表中更容易#[derive(Eq)]

\n\n

排序:PartialOrdOrd特征

\n\n

值的相对顺序是使用运算符<<=和计算>=>。要为您自己的类型实现这些,您必须实现该PartialOrd特征。

\n\n

在你能够实施之前PartialOrd,你必须先实施PartialEq

\n\n
impl PartialOrd for Person {\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        Some(self.cmp(other))    \n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

Ordering是一个具有以下值的枚举:

\n\n
pub enum Ordering {\n    Less,\n    Equal,\n    Greater,\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

partial_cmp返回 anOption而不是 an Ordering,因为有些类型的值不能总是进行比较,例如浮点数。NaNs 是不可表示的数字;诸如3.0 < NaNdon\xe2\x80\x99t 之类的表达式是有意义的。在这些情况下,partial_cmp返回None. 浮点值是标准库中唯一发生这种情况的情况。可以在这里找到更多内容。

\n\n

partial_cmp返回 an的事实Option<Ordering>会产生一个后果:可能无法将两个值 x 和 y 按确定的顺序放置。实际上,这意味着实施PartialOrd不足以使您的值可排序。您还需要实施Ord特征。

\n\n

在实施之前Ord,您必须首先实施PartialOrd,EqPartialEq

\n\n

对于我们的Person结构体,我们可以再次委托给我们的成员变量之一:

\n\n
impl Ord for Person {\n    fn cmp(&self, other: &Person) -> Ordering {\n        self.height.cmp(&other.height)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n


小智 6

对于初学者来说,你只能实现PartialOrd::partial_cmp,因为lt,le,gt,和ge有缺省的实现.此外,如果你实施Ord,你可以cmp像往常一样简单地实现并partial_cmp变得公正Some(self.cmp(other)).

但是,如果您只想委托某个字段的相等和排序概念,那么派生它会更好更容易:

#[derive(PartialOrd, Ord, PartialEq, Eq)]
struct MyType(isize);
Run Code Online (Sandbox Code Playgroud)