被Ord
坑了……
按文档,BinaryHeap
需要的是Ord
,但事实上BinaryHeap
需要的是derive(Ord)
保证trait bound,然后用PartialOrd
我闭眼按文档把Ord
改好之后,BinaryHeap
表示看不见,
……
Update: 想到了一个终极解决方案
trait Sealed;
pub trait Derive: Sealed;
pub struct True {_cannot_being_constructed:()}
pub struct False {_cannot_being_constructed:()}
impl Sealed for True{}
impl Sealed for False{}
impl Derive for True{}
impl Derive for False{}
pub type DeriveDefault = 按edition决定是True还是False;
trait Ord {
type Derive:Derive = DeriveDefault;
}
// 于是我们可以通过指定type Derive=Self来启动PartialOrd的impl
impl<T:Ord<Derive = True>> PartialOrd for T { ... }
留作练习:我们可以借此允许&str+&str -> String
吗?
(str
在core
里面,不允许分配内存,String
在alloc
里面,于是根据孤儿规则不能实现impl<'a> Add<Output = String> for &'a str
)
于是我愤而去写ACP,书上中下三策
下策是只写PartialOrd
,把Ord
的cmd
变成provided method(于是Ord
跟Copy/Eq
之流长得也就差不多了,直接impl Ord for Foo{}
解决一切)
中策是搞一个TotalOrd
和TotalEq
,然后让TotalOrd
自动实现Ord
和PartialOrd
,TotalEq
抄一遍PartialEq
但这玩意的语义是Eq
语义
上策是……中策怎么看怎么觉得,PartialEq+Eq
就应该叫Eq
,而不是TotalEq
,这个写成TotalOrd的东西,分明是PartialOrd+Ord
……所以为什么不直接把Ord/Eq
的实现改了呢?
以前是自底向上,先Partial之后实现自己,现在为什么不能自顶而下,从Eq/Ord
直接实现PartialEq/PartialOrd
呢
(甚至,为什么core不直接焊死一个impl<T:Copy> Clone for T
呢)……
由此,写就上中下三策,上表于libs-team
……只希望自己不是在打单机……
(p.s.外部链接不能输入github,否则就会像这样,直接被firefox屏蔽:)
Ext Link: https://github.com/rust-lang/libs-team/issues/425
评论区
写评论这玩意还有个类似的derive_ord_xor_partial_ord
目前的
Ord
全都是问题……一起去写ACP把这玩意扬了吧:)
👇
Foreverhighness: 让我想起了前段时间看到的一道 clippy lint non_canonical_partial_ord_impl
让我想起了前段时间看到的一道 clippy lint non_canonical_partial_ord_impl
哦,就是说:如果你已经有了 T: PartialOrd,此时你还要 T: Ord 的话,就说明 partial_cmp 大概率就是 Some() 里面包个 cmp 的实现(要不然你不会想要 T: Ord),所以我们直接提供一个默认实现免去你再复制粘贴的烦恼?
确实,我当时没看仔细,想得太激进了。关于这个方案的那一小节我总感觉是不是缺了
impl<T: Eq> TotalEq for T {}
和impl<T: Ord> TotalOrd for T {}
?这在逻辑上太奇怪了:对于已经实现了 trait A 的 T 实现 trait A...... what?? 我猜只有 T 的 trait 实现只在这一处定义的时候可以?
我的第一种方案是把cmp的全部methods改成provided methods,于是我们可以直接
由此
#[derive(Ord)]
的实现自然也就变成了简单的impl Ord for $ty {}
在这种情况下,可以近似将
Ord
当成是和Copy
/Eq
一样的marker来使用。错误标记Ord
的后果跟把String
标记成Copy
差不太多。这个并不是impl<T: Animal> Human for T {}
,因为标记需要手动完成。第二种实现的问题很小,因为老代码并不会impl TotalOrd和TotalEq,新代码在使用TotalOrd和TotalEq之后会自动放弃 impl Eq/Ord (转而使用TotalOrd/TotalEq提供的默认实现),所以这玩意大概率不会有冲突。
至于
Rust允许循环定义,因此你可以很轻松地实现如下代码:
至于改进derive……那是捎带的问题。
在我们统一了
impl<T:X> PartialX for T {...}
之后,derive是必须被解决掉的(目前一个很严重的问题是,Eq不是从PartialEq derive过去的,derive Eq会直接导致没有警告的逻辑问题。)最好的解法当然是把逻辑冲突变成编译错误。
👇
TinusgragLin: 确实,刚才我试了一下,在
Ord
和PartialOrd
实现相左的情况下,>, <, >=, <=
这些运算似乎都采用了PartialOrd
的实现,且如果partial_cmp
返回 None 的话,就会出现a >= b
和a <= b
全为假的情况。要我猜的话,这可能是为了在 IEEE754 浮点数只能实现 PartialOrd 的情况下,还能使用这些运算符比较方便地比较浮点数。我觉得你的第一种方案
impl<T: PartialOrd> Ord for T {}
不可行,T: PartialOrd
从字面上就说了 T 的某些值之间有可能没有序关系(比如 IEEE754 中的 NaN 和其他值),那么请问 T 为什么可以实现所有值之间都存在序关系的 Ord trait 呢?这就好像impl<T: Animal> Human for T {}
一样不符合逻辑;第二种方案似乎需要一个很大的 breaking change;第三种方案似乎有个逻辑问题,如果已经有Ord: PartialOrd
,为什么还要impl<T: Ord> PartialOrd for T {}
呢:T: Ord
就说明已经有T: PartialOrd
了呀。PartialOrd
,Ord
,PartialEq
,Eq
这四个确实在我刚开始入门 Rust 的时候也让我琢磨了一阵,我稍微 google 了一下,发现有关于优化这四个 trait derive 体验的提案:这个和这个,但是似乎都没了下文。确实,刚才我试了一下,在
Ord
和PartialOrd
实现相左的情况下,>, <, >=, <=
这些运算似乎都采用了PartialOrd
的实现,且如果partial_cmp
返回 None 的话,就会出现a >= b
和a <= b
全为假的情况。要我猜的话,这可能是为了在 IEEE754 浮点数只能实现 PartialOrd 的情况下,还能使用这些运算符比较方便地比较浮点数。我觉得你的第一种方案
impl<T: PartialOrd> Ord for T {}
不可行,T: PartialOrd
从字面上就说了 T 的某些值之间有可能没有序关系(比如 IEEE754 中的 NaN 和其他值),那么请问 T 为什么可以实现所有值之间都存在序关系的 Ord trait 呢?这就好像impl<T: Animal> Human for T {}
一样不符合逻辑;第二种方案似乎需要一个很大的 breaking change;第三种方案似乎有个逻辑问题,如果已经有Ord: PartialOrd
,为什么还要impl<T: Ord> PartialOrd for T {}
呢:T: Ord
就说明已经有T: PartialOrd
了呀。PartialOrd
,Ord
,PartialEq
,Eq
这四个确实在我刚开始入门 Rust 的时候也让我琢磨了一阵,我稍微 google 了一下,发现有关于优化这四个 trait derive 体验的提案:这个和这个,但是似乎都没了下文。