rust重载比较运算符

要重载比较运算符,需要为类型实现对应的trait。
重载==和!=,需要实现PartialEq或者Eq
重载<、<=、> 、 >=,需要实现PartialOrd或者Ord

一、Eq/PartialEq

为什么有两个trait呢?
因为相等关系有两种:一种是完全相等关系,一种是部分相等关系。
完全相等关系满足如下三个性质:
自反性:自己一定等于自己,即a=a
对称性:若有a=b,则有b=a
传递性:若有a=bb=c,则有a=c

部分相等关系只满足两个性质:
对称性:若有a=b,则有b=a
传递性:若有a=bb=c,则有a=c

在浮点数类型中有个特殊的值是NaN(Not-a-number),这个值与任何值都不等,包括自己NaN != NaN,它就违背了自反性。所以判断浮点数是否相等就只能使用部分相等关系。

部分相等是全相等关系的子集,也就是说,如果两个元素具有全相等关系,那它们之间也一定有部分相等关系。

(一)PartialEq

是部分相等关系。
这个Trait中定义了两个方法:

pub Trait PartialEq<Rhs: ?Sized = Self> {fn eq(&self, other: &Rhs) -> bool;fn ne(&self, other: &Rhs) -> bool {!self.eq(other)}
}

eq:两个值相等的话就返回true,需要使用者自行重载
ne:两个值不相等的话就返回true,默认已经实现了

所有的基本类型都实现了PartialEq

1.为自定义类型实现PartialEq
可使用#[derive(PartialEq)]由编译器自动实现

 #[derive(PartialEq)] pub struct Person {pub id: u32,pub name: String,pub height: f64,
}

也可以自己手动实现

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

实现时只需要实现eq方法即可,ne我们使用默认的。

实现了PartialEq,就自动重载了==和!=运算符,下面就可以判断是否相等了

fn main() {let p1 = Person {id: 0,name: "John".to_string(),height: 1.2,};let p2 = Person {id: 0,name: "Jack".to_string(),height: 1.4,};println!("p1 == p2 = {}", p1 == p2); // p1 == p2 = true
}

例子

fn main() {let f1 = f32::NAN;let f2 = f32::NAN;if f1 == f2 {println!("NaN竟然可以比较,这很不数学啊!")} else {println!("果然,虽然两个都是NaN,但是它们其实并不相等")}
}

2.比较不同的类型
给Rhs传入不同的类型,就能比较不同类型的相等性。
示例代码如下:

#[derive(PartialEq)]
enum WheelBrand { Bmw, Benz, Michelin, 
}
struct Car { brand: WheelBrand, price: i32, 
}
impl PartialEq<WheelBrand> for Car {fn eq(&self, other: &WheelBrand) -> bool { self.brand == *other }
}
fn main() {let car = Car { brand: WheelBrand::Benz, price: 10000 };let wheel = WheelBrand::Benz; // 比较struct和enumassert!(car == wheel);// assert!(wheel == car); // 无法反过来比较
}

代码仅实现了Car与Wheel的相等性比较,若要反过来比较,还得提供反向的实现,如下:

impl PartialEq<Car> for WheelBrand {fn eq(&self, other: &Car) -> bool {*self == other.brand }
}

(二)Eq

就是完全相等关系

这个trait继承了PartialEq,但没有添加新的方法,这个Trait只是告诉编译器,这是个完全相等关系而非部分相等关系。

pub Trait Eq:PartialEq{}

在标准库中,只有f32和f64没有实现Eq

1.为自定义类型实现Eq
实现Eq不需要额外的代码,只需要实现PartialEq,并添加#[derive(Eq)]就可以了。

实现了Eq的类型自然也重载了 == 和!=运算符,下面就可以判断是否相等了
Rust中HashMap的key要求实现Eq,也就是要能完全相等,而浮点数由于没有实现Eq,因此不能用于HashMap的key

二、Ord / PartialOrd

大小关系也有两种:一种是全序关系,一种是偏序关系。
全序关系有以下性质:

完整的不对称性total antisymmetry:a < b,a == b,a > b这三种结果只有一个是真;
可传递性transitive:如果a < b且b < c那么a < c;

偏序关系有以下性质:

不对称性antisymmetry:如果a < b那么 !(a > b);
可传递性transitive:如果a < b且b < c那么a < c;

还是因为特殊值NaN,NaN < 0 == false并且NaN > 0 == false并且(NaN == 0) == false

(一)PartialOrd

偏序关系

PartialOrd继承了PartialEq,并且新定义了几个方法

pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> {fn partial_cmp(&self, other: &Rhs) -> Option<Ordering>;fn lt(&self, other: &Rhs) -> bool {matches!(self.partial_cmp(other), Some(Less))}fn le(&self, other: &Rhs) -> bool {matches!(self.partial_cmp(other), Some(Less | Equal))}fn gt(&self, other: &Rhs) -> bool {matches!(self.partial_cmp(other), Some(Greater))}fn ge(&self, other: &Rhs) -> bool {matches!(self.partial_cmp(other), Some(Greater | Equal))}
}

partial_cmp:需要使用者重载,返回两值的比较结果;
lt,le,gt,ge:默认定义好了;

标准库里的所有基本类型都已实现该Trait

1.为自定义类型实现PartialOrd
要实现PartialOrd,必须同时实现PartialEq

可使用#[derive(PartialOrd)]的方法实现该Trait,也可手动实现

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

实现PartialOrd只需要实现partial_cmp方法即可,lt(),le(),gt() , ge() 使用默认的

比较结果为Ordering枚举类型:

pub enum Ordering {Less = -1,Equal = 0,Greater = 1,
}

为什么partial_cmp这个方法返回值类型是个Option, 而非直接是一个Ordering值类型?
这仍然与浮点数类型有关, 因为NaN不是一个可以表示的数值, 诸如:3.0 < NaN这样的表达式毫无意义!对于这种情况,partial_cmp就会返回None。
partial_cmp返回一个Option导致一个结果,当结果为None时, 无法决定两个值的排序,即x和y会处于不确定排序。只实现PartialOrd还不足以使你的自定义类型可排序,你还需要实现Ord。

实现了PartialOrd的类型,会自动重载 <、<=、> 、 >= 运算符,下面就可以比较了
例子

fn main() {let p1 = Person {id: 0,name: "John".to_string(),height: 1.2,};let p2 = Person {id: 0,name: "Jack".to_string(),height: 1.4,};println!("p1 < p2 = {}", p1 < p2);println!("p1 <= p2 = {}", p1 <= p2);println!("p1 > p2 = {}", p1 > p2);println!("p1 >= p2 = {}", p1 >= p2);let x: f64 = std::f64::NAN;let y = 1.0f64;assert_eq!(x.partial_cmp(&y), None);
}

2.比较不同类型

(二)Ord

全序关系
Ord继承了PartialOrd和Eq,并且新定义了几个方法:

pub trait Ord: Eq + PartialOrd<Self> {fn cmp(&self, other: &Self) -> Ordering;fn max(self, other: Self) -> Self{...}fn min(self, other: Self) -> Self{...}fn clamp(self, min: Self, max: Self) -> Self{...}
}

cmp:需要使用者重载本方法,返回两值的比较结果;
max,min,clamp:已经定义好了;

在标准库中,只有f32和f64没有实现Ord

1.为自定义类型实现Ord
要实现Ord,必须要同时实现PartialOrd和Eq。实现PartialEq,PartialOrd以及Ord时要特别注意彼此之间不能冲突
例子
vector中的sort方法要求类型实现了Ord

use std::cmp::Ordering;
#[derive(Debug,Eq)]
pub struct Person {pub id: u32,pub name: String,pub height: f64,
}
impl PartialEq<Self> for Person {fn eq(&self, other: &Self) -> bool { self.id == other.id }
}
impl PartialOrd for Person {fn partial_cmp(&self, other: &Self) -> Option<Ordering> {self.id.partial_cmp(&other.id)}
}
impl Ord for Person {fn cmp(&self, other: &Self) -> Ordering {self.id.cmp(&other.id)}
}

实现Ord只需要实现cmp方法即可,max() 和min()使用默认的。

例子

enum BookFormat3 {Paperback,Hardback,Ebook,
}struct Book3 {name: String,format: BookFormat3,
}// -- 先实现 PartialEq
impl PartialEq for Book3 {fn eq(&self, other: &Book3) -> bool {self.name == other.name// 这里假设format字段不要求比较}
}// -- 再实现 Eq
impl Eq for Book3 {}// -- 再实现 Ord
impl Ord for Book3 {fn cmp(&self, other: &Book3) -> Ordering {// 直接调用name(String)的cmp方法(当需要实现Ord时,成员字段一般都实现了Ord,可直接调用其cmp方法)self.name.cmp(&other.name)}
}// -- 最后实现 PartialOrd
impl PartialOrd for Book3 {fn partial_cmp(&self, other: &Book3) -> Option<Ordering> {// 直接调用上面实现的cmp方法Some(self.cmp(&other))}
}

实现Ord的时候需要同时实现PartialOrd,PartialOrd的partial_cmp()内部调用的是Ord的cmp(),理由是既然一开始就想要为类型实现Ord,说明类型是能够得出一个肯定结果的(非None)

实现了Ord之后,会自动重载 <、<=、> 、 >= 运算符,下面就可以比较了
例子

use std::cmp::Ordering;
let x = 1;
let y = 2;
assert_eq!(x.cmp(&y), Ordering::Less);
assert_eq!(y.cmp(&x), Ordering::Greater);
assert_eq!(x.cmp(&x), Ordering::Equal);
assert_eq!((-3).clamp(-2, 1), -2);
assert_eq!(0.clamp(-2, 1), 0);
assert_eq!(2.clamp(-2, 1), 1);
assert_eq!(1.min(2), 1);
assert_eq!(2.min(2), 2);
assert_eq!(1.max(2), 2);
assert_eq!(2.max(2), 2);

例子

fn main() {let mut v = vec![Person {id: 3,name: "".to_string(),height: 3.0,},Person {id: 2,name: "".to_string(),height: 4.0,},Person {id: 1,name: "".to_string(),height: 5.0,},];v.sort();println!("{:?}", v); 
}

2.比较不同类型

三、关于效率

你也许好奇,到底是自己手动实现的效率高, 还是auto-derive效率高?这很难说,不能一概而论,比如:如果你明确定知道一个大struct中只有少数几个成员与此比较直接相关,或者说有决定性, 那么你自己手动实现的版本很可能优于auto-derive版本, 因为auto-derive版本通常会依次比较所有成员,很可能做了无用功。换一个角度说, 尽管auto-derive版本可能会依次检查比较每一个struct成员, 但是因为它可以采用布尔表达式的短路原则, 也许检查第一个成员就停止了,因此也很有可能快于自定义实现版。但是Hash是一个例外,它不允许短路原则,必须所有成员依次都要哈希一次才可以,不能偷懒,但如果你能够只哈希1或2个简单成员而不是大量字符串成员,那么您将很容易击败auto-derive默认实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/119032.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

30天精通Nodejs--第二天:模块系统与npm

深入了解Node.js&#xff1a;模块系统与npm Node.js作为一款强大的服务器端JavaScript运行环境&#xff0c;模块系统和npm&#xff08;Node Package Manager&#xff09;是其成功的重要组成部分。为我们平时提供了便捷的工具和资源&#xff0c;使得在Node.js平台上构建应用变得…

现在java和大数据选什么?

现在java和大数据选什么&#xff1f; 到底是选择大数据还是JAVA&#xff1f;”相信这个问题困惑着许多转行待定人士和高校专业待选的学生。 在普通人眼里可能会觉得这两个专业或者行业没啥区别&#xff0c;都是IT里的&#xff0c;能有啥大不同。这是第一层。最近很多小伙伴找我…

【Linux】MAC帧协议 + ARP协议

文章目录 &#x1f4d6; 前言1. 数据链路层2. MAC帧格式3. 再谈局域网4. ARP协议4.1 路由器的转发过程&#xff1a;4.2 ARP协议格式&#xff1a; 5. 如何获得目的MAC地址 &#x1f4d6; 前言 在学完网络层IP协议之后&#xff0c;本章我们将继续向下沉一层&#xff0c;进入到数…

深入浅出排序算法之希尔排序

目录 1. 原理 2. 代码实现 3. 性能分析 1. 原理 希尔排序法又称缩小增量法。希尔排序法的基本思想是&#xff1a;先选定一个整数&#xff0c;把待排序文件中所有记录分成个组&#xff0c;所有距离为的记录分在同一组内&#xff0c;并对每一组内的记录进行排序。然后&#xf…

Flink 维表关联

1、实时查询维表 实时查询维表是指用户在 Flink 算子中直接访问外部数据库&#xff0c;比如用 MySQL 来进行关联&#xff0c;这种方式是同步方式&#xff0c;数据保证是最新的。但是&#xff0c;当我们的流计算数据过大&#xff0c;会对外 部系统带来巨大的访问压力&#xff0…

ui设计要学插画吗?优漫动游

现如今很多UI设计培训班都开设了商业插画的课程&#xff0c;有不少同学表示真的要学吗&#xff1f;商业插画都有什么用处呢&#xff1f;今天我们就来给大家介绍一下商业插画在UI设计中的运用。 ui设计要学插画吗&#xff1f;   商业插画属于实用型插画&#xff0c;是一种…

详解预处理(1)

目录 预定义符号 预处理指令#define #define定义符号 #define定义宏 #define替换规则 #和##&#xff08;C语言预处理操作符&#xff09; # ## 带副作用的宏参数 宏和函数的对比 命名约定 在之前我们学习了一个文本文件.c生成一个可执行程序。今天我们详细讲解其中的…

腾讯云国际站服务器端口开放失败怎么办?

腾讯云服务器是腾讯公司推出的一种云服务&#xff0c;用户能够经过这种方式在互联网上进行数据存储和计算。然而&#xff0c;用户在运用腾讯云服务器时或许会遇到各种问题&#xff0c;其间端口敞开失利是一个常见问题。本文将具体介绍如何解决腾讯云服务器端口敞开失利的问题。…

01 # 手写 new 的原理

new 做了什么? 在构造器内部创建一个新的对象这个对象内部的隐式原型指向该构造函数的显式原型让构造器中的 this 指向这个对象执行构造器中的代码如果构造器中没有返回对象&#xff0c;则返回上面的创建出来的对象 手写 new 的过程 new 是一个运算符&#xff0c;只能通过函…

CSS隐藏元素的N种方法,你知道哪一种最适合你?

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 ⭐ 专栏简介 &#x1f4d8; 文章引言 一、前…

Camtasia2024永久激活码

真的要被录屏软件给搞疯了&#xff0c;本来公司说要给新人做个培训视频&#xff0c;想着把视频录屏一下&#xff0c;然后简单的剪辑一下就可以了。可谁知道录屏软件坑这么多&#xff0c;弄来弄去头都秃了&#xff0c;不过在头秃了几天之后&#xff0c;终于让我发现了一个值得“…

el-date-picker日期选择器奇怪的问题解决

问题描述&#xff1a;点击日期选择器&#xff0c;出现日历下拉框&#xff0c;选择了日期后绑定的value值不变&#xff1b;绑定 change 事件&#xff0c;监听不到 解决方案&#xff1a;添加input事件&#xff0c;$forceUpdate() <el-date-pickerv-model"value1"ty…

用Python做数据分析之数据处理及数据提取

1、数据预处理 第四部分是数据的预处理&#xff0c;对清洗完的数据进行整理以便后期的统计和分析工作。主要包括数据表的合并&#xff0c;排序&#xff0c;数值分列&#xff0c;数据分组及标记等工作。 1&#xff09;数据表合并 首先是对不同的数据表进行合并&#xff0c;我们…

【Linux】gdb调试

目录 进入调试查看代码运行代码断点打断点查断点删断点从一个断点转跳至下一个断点保留断点但不会运行该断点 退出调试逐过程逐语句监视跳转至指定行运行结束当前函数 进入调试 指令&#xff1a;gdb 【可执行文件】&#xff1a; 查看代码 &#xff1a;l 【第几行】如果输入指…

安全设备

一.防火墙 5层应用层 防火墙 4层 udp tcp 协议 华为 厂商 华为 h3 1.区域划分 Dmz 停火区 Untrust 不安全区域 Trust 安全区域 防火墙 默认禁止所有 二.Waf Web 应用防火墙 放到web前面 产品 雷池 绿盟 软件 安…

递归神经网络 (RNN)

弗朗西斯科佛朗哥 一、说明 循环神经网络非常有趣&#xff0c;因为与前馈网络不同&#xff0c;在前馈网络中&#xff0c;数据只能在一个方向上传播&#xff0c;每个神经元可以与连续层的一个或多个神经元连接&#xff0c;在这种类型的网络中&#xff0c;神经元还可以环回自身或…

MAXScript - tyFlow for 3dsMax

MAXScript - tyFlow for 3dsMax tyFlow 信息 tyFlow_version(): 返回当前安装的tyFlow DLO文件的版本号。tyFlow_found_in_scene(): 返回有关场景中活动tyFlow类&#xff08;对象、修改器等&#xff09;数量的信息。 tyFlow 许可 tyFlow_activate_license(): 尝试激活tyFlo…

安卓端GB28181设备接入模块如何实现实时位置订阅(MobilePosition)

技术背景 实时位置&#xff08;MobilePosition&#xff09;订阅和上报&#xff0c;对GB28281设备接入终端尤其重要&#xff0c;如移动单兵设备、执法记录仪、智能安全帽、车载终端等&#xff0c;Android国标接入设备通过获取到实时经纬度信息&#xff0c;按照一定的间隔上报到…

竞赛选题 深度学习卫星遥感图像检测与识别 -opencv python 目标检测

文章目录 0 前言1 课题背景2 实现效果3 Yolov5算法4 数据处理和训练5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **深度学习卫星遥感图像检测与识别 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐…