Rust中derive宏的作用及常用trait

在Rust代码经常可以看到在struct的上面,有一行#[derive(Clone, Debug)]这样的代码。dervice是Rust的内置宏,可以自动为struct或是enum实现某些的trait。
在下面的代码中,Book struct 通过derive宏自动实现了Debug、Clone和PartialEq这三个trait。

/// Defines a Column for an Entity
#[derive(Debug, Clone, PartialEq)]
pub struct Book {pub title: String,pub isbn : String,pub price: i32,pub author: String,
}

所谓自动实现,就是不用您自己写实现代码。

本文会介绍在Rust中常见的几个trait。

Debug trait

Debug trait为例,它应该是rust中最常用的trait了,包含一个方法fmt,是使用指定的Formatter来格式struct或enum中的值。

fn fmt(&self, f: &mut Formatter<'_>) -> Result;

当使用derive自动Debug实现时,Rust的编译器会自动生成实现Debugtrait的代码,可以减少代码编写的工作量。

我们经常要在代码加入一些debug的日志,比如要打印Book实例的具体内容。

println!("{:#?}",book);

在这里println宏会调用Book的fmt方法,得到格式化后的结果,然后输出给stdout。如果Book没有实现Debugtrait,这里就会编译错误。

Clone trait

看名字大家也就可以猜到,这个trait是用来复制实例的。在Rust中什么情况下需要clone一个实例呢?为什么默认为实例实现这个trait呢?

  1. 显式控制 :Rust强调显式性和安全性。所以默认并没有为所有的类型实现这个trati,它确保开发人员知道并明确允许克隆行为。这有助于防止由于不加选择地克隆而导致的意外性能问题或意外行为。

  2. 避免借用:在Rust中,通过引用(borrowing)传递值是避免不必要复制并维护所有权语义的默认方式,然而,在需要创建具有自己所有权的新实例的情况下,Clone提供了一种无需借用的方法。这种情况在新手刚使用Rust的时候经常会碰到,常会碰到编译器提示,所有权已经在某处move了,提示需要clone这个实例。

#[derive(Clone)]
struct Person {name: String,age: u32,
}fn main() {let person = Person {name: String::from("Alice"),age: 25,};let cloned_person = person.clone(); //这一行如果不调用clone,则会发生所有权的转移。那么下一行的代码就会无法编译。// 原始对象仍然有效println!("Original: {}, {}", person.name, person.age);// 克隆对象可用println!("Clone: {}, {}", cloned_person.name, cloned_person.age);
}
  1. 深拷贝:如果定义的结构体中不仅包含基本类型,还包含其它结构体,则在Clone的时候 ,通常希望创建深拷贝,这意味着不仅复制顶层结构,还要复制所有嵌套数据。Clone trait提供了一种类型定义如何克隆的方法,允许它们实现自定义的深度复制行为。

  2. 定制克隆:有时候我们Clone的时候,并不希望Clone原实例的所有的值,可能只希望部分数值被Clone到新实例(具体场景当用到的时候自然就会知道)。

上面的4种场景,除了场景4其它都可以直接用devive宏来实现,第4种场景就需要手动实现Clone trait,实现Clone的逻辑。

PartialEq trait

在Rust里 PartialEqEq这两个trait也挺让人迷惑的。

PartialEq,故名思义,是部分相等。这个trait有两个方法:

pub trait PartialEq<Rhs = Self>whereRhs: ?Sized,{// Required methodfn eq(&self, other: &Rhs) -> bool;// Provided methodfn ne(&self, other: &Rhs) -> bool { ... }
}

只需要实现eq这个方法即可。那么Partial体现在哪儿呢?比如有个Book结构体,包含isbnformat两个字段,只要isbn相等,就可以认为两个Book是相等的,从这个意义上看,只有部分字段相等就可以认为相等,所以称Partial

enum BookFormat {Paperback,Hardback,Ebook,
}struct Book {isbn: i32,format: BookFormat,
}impl PartialEq for Book {fn eq(&self, other: &Self) -> bool {self.isbn == other.isbn}
}let b1 = Book { isbn: 3, format: BookFormat::Paperback };
let b2 = Book { isbn: 3, format: BookFormat::Ebook };
let b3 = Book { isbn: 10, format: BookFormat::Paperback };

另外,跟Eq trait相比,PartialEq不满足自反性。
所谓自反性,就是一个类型的所有实例应该跟它自己相等,如果不是这个类型就不满足Eq,它就是PartialEq。这样说比较抽象,举个例子来说明。

fn main() {let f1 = 3.14;is_eq(f1);is_partial_eq(f1)
}fn is_eq<T: Eq>(f: T) {}
fn is_partial_eq<T: PartialEq>(f: T) {}

运行上面的代码,会发现float并没有实现Eq,这很奇怪吧?

 is_eq(f1);----- ^^ the trait `Eq` is not implemented for `{float}`

这是因为浮点数有一个特殊的值 NaN,它是无法进行相等性比较的,也就是NaN == NaN是不成立的。如果你的struct也有类似的情况,那就不能实现Eq trait。

这两个trait都可以用derive宏来自动实现。当用derive来实现时,如果要实现Eq trait,那么所有的字段都必须实现Eq,如果包含浮点数这样没有实现Eq的字段,那么是无法实现Eq的。比如下面的代码是无法编译的:

#[derive(Debug, PartialEq, Eq)]
struct Book {isbn: String,price: f32,
}

编译器会建议你把price改成i32这种实现了Eq的类型。

PartialOrd, Ord

PartialOrdOrd这对Trait的应用场景跟PartialEqEq非常相似。
PartialOrd用于类型只能部分进行比较的场景,Ord则要求类型所有的部分都能进行比较。
比如上面例子中的浮点类型中的NaN,是不能比较的,所以包含浮点数的类型,就不能实现Ord,只能实现PartialOrd

pub trait PartialOrd<Rhs = Self>: PartialEq<Rhs>
whereRhs: ?Sized,
{// Required methodfn partial_cmp(&self, other: &Rhs) -> Option<Ordering>;// Provided methodsfn lt(&self, other: &Rhs) -> bool { ... }fn le(&self, other: &Rhs) -> bool { ... }fn gt(&self, other: &Rhs) -> bool { ... }fn ge(&self, other: &Rhs) -> bool { ... }
}

ltlegtge可能分别通过 <<=>,和 >= 这些操作符来调用。可见Rust是通过trait来支持操作符重载的。

总结一下,上述的traits在rust里被称为Derivable Traits,中文叫可派生的 trait。这些trait是标准库中定义的,可以通过derive在类型上实现。这些trait具有默认行为,因此可以通过简单的derive宏来自动生成对应的实现代码。Derivable Traits允许程序员轻松地为他们的类型自动生成一些常见trait的实现代码,提高了开发效率并确保了一致性。

本文同时发在我的个人网站上:https://www.renhl.com/posts/2024/02/20/rust-derivable-traits/

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

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

相关文章

idea操作

我的一些总结&#xff0c;后续会陆续添加 代码编辑相关&#xff1a; 格式化代码&#xff1a;Ctrl Alt L (Windows) / Command Option L (Mac) 快速修复&#xff1a;Alt Enter 查找定义&#xff1a;Ctrl 鼠标左键单击 (Windows) / Command 鼠标左键单击 (Mac) 多行编辑…

vue3 打印局部网页、网页下载为图片、下载为pdf-自动分页,几行代码搞定

经常有一些需求&#xff0c;要将网页保存为一张图片&#xff0c;感觉异常困难&#xff0c;这里发现一个简单的办法。 这么简单&#xff0c;直接一句哇塞&#xff0c;老板&#xff1a;马上完成任务。 先安装几个依赖 npm i howuse html2canvas jspdf 下载图片代码 <button …

L1 - 006 连续因子

思路&#xff1a;1.要求最长的连续因子序列&#xff0c;我们需要知道序列的长度和序列的起点。 2.对于起点 i 来说&#xff0c;他不能超过 n 的平方根&#xff0c;在循环时从 2 到 sqrt(n) 。用到变量&#xff1a;记录个数num&#xff0c;起点start&#xff0c;最大个数maxnum…

Qt篇——QChartView获取鼠标停留位置的数值

需求&#xff1a;鼠标停留在QChartView上时&#xff0c;想要计算停留位置的数值。 一开始的方法是想要通过鼠标移动事件计算鼠标在QChartView上的坐标&#xff0c;在换算成数值&#xff0c;后来发现QChartView中除了图表数据&#xff0c;还有坐标轴与坐标轴数值标签占了高度&a…

How to install teams in ubuntu

Download deb file download link: https://mirrors.sdu.edu.cn/spark-store-repository/store/office/teams/ install deb sudo apt install ./teams_1.5.00.23861_amd64.deb open and login teams.

基于单片机的车载酒精含量自检系统设计与实现

摘要:调查显示,大约50%的交通事故与酒后驾车有关,酒后驾车已成为车祸致死的首要原因。为从根本上杜绝酒后驾车,设计了一款基于STC89C52 单片机的车载酒精含量自检系统,该系统能很好地解决酒驾问题,控制简单、使用方便,具有很好的应用价值。 关键词:STC89C52 单片机;车…

ubuntu编译rk3588异常

问题现象 在ubuntu上编译 rk3588 的kernel时&#xff0c;报如下错误&#xff1a; LZ4C arch/arm64/boot/Image.lz4 Incorrect parameters Usage : lz4 [arg] [input] [output] input : a filename with no FILE, or when FILE is - or stdin, read sta…

JavaEE企业开发新技术2

目录 2.7 Field类的基本概念 文字性概念描述&#xff1a; Field类 2.8 Field的基本操作-1 2.9 Field的基本操作-2 分析&#xff1a; 2.10 Field 的综合练习 总结&#xff1a; 和equals的区别&#xff1a; 使用 比较 使用equals比较 2.7 Field类的基本概念 文字性…

.NET高级面试指南专题十七【 策略模式模式介绍,允许在运行时选择算法的行为】

介绍&#xff1a; 策略模式是一种行为设计模式&#xff0c;它允许在运行时选择算法的行为。它定义了一系列算法&#xff0c;将每个算法封装到一个对象中&#xff0c;并使它们可以互相替换。这使得算法可独立于使用它的客户端变化。 原理&#xff1a; 策略接口&#xff08;Strat…

CVPR2024 | 大核卷积新高度101x101,美团提出PeLK

https://arxiv.org/pdf/2403.07589.pdf 本文概述 最近&#xff0c;一些大核卷积网络以吸引人的性能和效率进行了反击。然而&#xff0c;考虑到卷积的平方复杂度&#xff0c;扩大内核会带来大量的参数&#xff0c;而大量的参数会引发严重的优化问题。由于这些问题&#xff0c;当…

安卓之四大组件

组件描述Activity(活动)在应用中的一个Activity可以用来表示一个界面&#xff0c;意思可以理解为“活动”&#xff0c;即一个活动开始&#xff0c;代表 Activity组件启动&#xff0c;活动结束&#xff0c;代表一个Activity的生命周期结束。一个Android应用必须通过Activity来运…

蓝桥杯刷题|01入门真题

[蓝桥杯 2020 省 AB1] 解码 题目描述 小明有一串很长的英文字母&#xff0c;可能包含大写和小写。 在这串字母中&#xff0c;有很多连续的是重复的。小明想了一个办法将这串字母表达得更短&#xff1a;将连续的几个相同字母写成字母 出现次数的形式。 例如&#xff0c;连续…

Xcode 15.3 Archive失败

Xcode 15.3 Archive失败 背景 升级 Xcode 到 15.3&#xff0c;真机运行正常。打包的时候发现 Archive 失败。 提示&#xff1a; Call parameter type does not match function signature! 仔细看报错里是和HandyJSON相关的提示。 解决 起初以为和 Pod 库有关系&#xff0c;…

docker容器启动rabbitmq

docker容器启动rabbitmq 一、RabbitMQ部署1.1.在线拉取mq镜像1.2.运行mq容器1.3.访问mq 二、RabbitMQ的集群2.1.集群分类2.1.设置 /etc/hosts文件 endl 一、RabbitMQ部署 1.1.在线拉取mq镜像 # 在线拉取 docker pull rabbitmq:3-management1.2.运行mq容器 docker run \ -e R…

什么是单向数据流

单向数据流是一种数据流动的模式&#xff0c;通常用于前端框架&#xff08;如 React、Vue 等&#xff09;中。在单向数据流中&#xff0c;数据只能从一个方向流向另一个方向&#xff0c;不会出现数据的双向流动。这种模式有助于简化数据的管理和状态的维护&#xff0c;提高代码…

Kafka-生产者报错javax.management.InstanceAlreadyExistsException

生产者发送消息到 kafka 中,然后控制台报错 然后根据日志查看 kafka 的源码发现了问题原因 说的是MBean已经注册了,然后报异常了,这样就会导致生产者的kafka注册失败, 原因是项目上生产者没有配置clientId,默认都是空导致的, 多个生产者(项目)注册到kafka集群中的 id 都相同。 …

微服务分布式基于Springcloud的拍卖管理系统597wx

越来越多的用户利用互联网获得信息&#xff0c;但各种信息鱼龙混杂&#xff0c;信息真假难以辨别。为了方便用户更好的获得信息&#xff0c;因此&#xff0c;设计一种安全高效的拍卖管理系统极为重要。 为设计一个安全便捷&#xff0c;并且使用户更好获取拍卖管理系统&#xff…

在Linux/Ubuntu/Debian中使用lsof和fuser查看/解除文件占用

“lsof”&#xff08;列出打开的文件&#xff09;和“fuser”&#xff08;使用文件识别进程&#xff09;类似&#xff0c;但功能略有不同&#xff1a; 1.lsof&#xff1a; - lsof 列出有关进程打开的文件的信息。 它提供有关打开哪些文件、哪些进程正在访问这些文件以及其他相…

探寻殷墟文化,基于YOLOv5全系列【n/s/m/l/x】参数模型开发构建殷墟考古场景下的甲骨文字符检测识别分析系统

甲骨文是一种非常历史悠久的古老文字&#xff0c;在前面我们基本上很少有涉及这块的内容&#xff0c;最近正好在做文字相关的项目开发研究&#xff0c;就想着基于甲骨文的场景来开发对应的检测识别系统&#xff0c;在前文中我们基于系列YOLO算法模型开发构建了在仿真数据实验场…

C#,图论与图算法,图着色问题(Graph Coloring)的威尔士-鲍威尔(Welch Powell Algorithm)算法与源代码

Welsh, D.J.A. and Powell, M.B. (1967) An Upper Bound for the Chromatic Number of a Graph and Its Application to Timetabling Problems. 《The Computer Journal》, 10, 85-86. 《The Computer Journal》 1 图着色算法概述 1967年&#xff0c;Welsh和Powell算法引入了…