为类型实现 Ord 很尴尬? [英] Implementing Ord for a type is awkward?

查看:56
本文介绍了为类型实现 Ord 很尴尬?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个新类型,我想实现Ord:

use std::cmp::{Ord, Ordering};结构我的类型(大小);MyType { 的 impl 命令fn cmp(&self, &other: Self) ->订购{让 MyType(ref lhs) = *self;让 MyType(ref rhs) = *other;lhs.cmp(rhs)}}

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

error[E0277]: trait bound `MyType: std::cmp::PartialOrd` 不满足-->src/main.rs:5:6|5 |MyType { 的 impl 命令|^^^ 无法比较 `MyType` 和 `MyType`|= help: 特性 `std::cmp::PartialOrd` 没有为 `MyType` 实现错误[E0277]:不满足特征绑定`MyType: std::cmp::Eq`-->src/main.rs:5:6|5 |MyType { 的 impl 命令|^^^ 特性 `std::cmp::Eq` 没有为 `MyType` 实现

当我实现PartialEqEqPartialOrd (gt(), lt()eq()ge()le() 等),一切正常,但如果我提供cmp,我们可以推断出lt()eq() 之类的函数!这是多余的!我不喜欢这个!

在查看文档时,我在Ord的定义:

pub trait Ord: Eq + PartialOrd

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

这是如何在 Rust 中完成的?我希望不是这样...

解决方案

对于您的具体情况,我会使用 #[derive]:

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]结构我的类型(大小);fn 主(){让 a = MyType(5);让 b = MyType(6);println!("{:?}", a.cmp(&b))}

如果您需要对 Ord 实现进行特殊处理,您仍然需要写出该代码,没有什么可以拯救我们的!如果这样做有意义,您仍然可以为其他特征派生实现.

你问题的另一部分含糊不清,所以我会从两个方面回答:

<块引用>

为什么带有 PartialOrd 的任何东西都不能自动提供 Ord?

让我们看看 PartialOrdOrd.

PartialOrd 说:比较必须满足 antisymmetry传递性",而 Ord 说:形成总顺序".这些是数学术语,我不会像维基百科那样描述它们.

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

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

<块引用>

为什么带有 Ord 的任何东西都不能自动提供 PartialOrd?

这里的问题是PartialOrd 的类型比Ord 的类型多.如果我们要求所有东西都是 Ord,那么我们就不能有 任何 浮点比较,因为它们没有总顺序,或者我们必须给出拥有一个完整的订单并失去它提供的安全性.

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

#[derive(Debug, Copy, Eq, Ord)]结构我的类型(大小);

<块引用>

当我实现 PartialOrd (gt(), lt(), eq(), >ge(), le() ...)

注意PartialOrd确实有默认实现,你只需要实现partial_cmp.其他是出于易用性或可能的性能原因.

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...

解决方案

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 partial_cmp. The others are there for ease-of-use or possibly performance reasons.

这篇关于为类型实现 Ord 很尴尬?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆