为类型实现 Ord 很尴尬? [英] Implementing Ord for a type is awkward?
问题描述
我有一个新类型,我想实现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` 实现
当我实现PartialEq
、Eq
和PartialOrd
(gt()
, lt()
、eq()
、ge()
、le()
等),一切正常,但如果我提供cmp
,我们可以推断出lt()
和eq()
之类的函数!这是多余的!我不喜欢这个!
在查看文档时,我在Ord
的定义:
pub trait Ord: Eq + PartialOrd
这看起来像继承自 Eq
和 PartialOrd
的特征.为什么 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
?
让我们看看 PartialOrd
和 Ord
一>.
PartialOrd
说:比较必须满足 antisymmetry 和 传递性",而 Ord
说:形成总顺序一个>".这些是数学术语,我不会像维基百科那样描述它们.
但是,我们可以使用浮点数作为示例.浮点数有一个叫做 NaN
的特殊值.与这个值进行比较是很棘手的.例如,所有 1.0
1.0 == NaN
和 1.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 withPartialOrd
?
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 withOrd
?
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屋!