30天拿下Rust之Trait

概述

        在Rust中,Trait是一个核心概念,它允许我们定义类型应该具有的行为。Trait类似于其他语言中的接口,但Rust的Trait更为强大和灵活。它不仅定义了一组方法,还允许我们指定方法的默认实现、泛型约束和继承。通过Trait,我们可以定义一组方法的签名和关联类型,使得不同的类型能够共享相同的行为接口,进而支持多态性。

定义Trait

        在Rust中,Trait(特征)用于定义一组方法签名,这些方法可以由任何实现了该Trait的类型来提供具体的实现。Trait提供了一种抽象机制,允许我们编写与具体类型无关的通用代码。

        在Rust中定义Trait的基本步骤如下。

        1、声明Trait:使用trait关键字来声明一个新的Trait。

        2、定义方法:在Trait体内,列出所有该Trait类型必须实现的方法,包括:方法名、参数列表和返回类型。

        3、可选的默认实现:可以为Trait中的方法提供默认实现,这样实现该Trait的类型可以选择是否覆盖这些默认实现。

        在下面的示例代码中,我们定义了一个名为Shape的Trait,它有两个方法:area()和perimeter()。area()方法没有默认实现,这意味着,任何实现Shape Trait的类型都必须提供这个方法的具体实现。perimeter()方法有一个默认实现,返回值为0.0。实现这个Trait的类型可以选择提供自己的实现来覆盖这个默认值,当然,也可以不覆盖。

trait Shape {// 定义一个没有默认实现的方法fn area(&self) -> f64;// 定义一个带有默认实现的方法fn perimeter(&self) -> f64 {// 这里是默认实现,但可以被实现该Trait的类型覆盖0.0}
}

实现Trait

        一旦我们定义了某个Trait,就可以为具体的类型实现它。这通常通过impl关键字来完成,后面跟着Trait名称和类型名称。

        在下面的示例代码中,我们定义了一个Circle结构体,并为它实现了Shape Trait。我们提供了area()和perimeter()方法的具体实现,其中,perimeter()方法覆盖了Shape Trait中定义的默认实现。现在,任何接受Shape Trait作为参数或返回值的函数都可以使用Circle类型的实例,因为Circle实现了Shape Trait。正是这种灵活性,使得Trait成为Rust中实现代码复用和抽象的重要工具。

impl Shape for Circle {// 提供area方法的具体实现fn area(&self) -> f64 {std::f64::consts::PI * self.radius * self.radius}// 覆盖Shape Trait中perimeter方法的默认实现fn perimeter(&self) -> f64 {2.0 * std::f64::consts::PI * self.radius}
}

        在Rust中,一个类型还可以实现多个Trait。

trait Fly {fn fly(&self);
}trait Swim {fn swim(&self);
}struct Duck {name: String,
}impl Fly for Duck {fn fly(&self) {println!("{} is flying", self.name);}
}impl Swim for Duck {fn swim(&self) {println!("{} is swimming", self.name);}
}fn main() {let duck = Duck { name: "Donald".to_string() };duck.fly();duck.swim();
}

        在上面的示例代码中,Duck结构体实现了Fly和Swim这两个Trait,因此它既可以飞,也可以游泳。这允许我们在使用Duck实例时,根据需要调用相应的接口方法。

泛型约束

        泛型函数和泛型结构体通常需要对其类型参数施加一些约束,以确保它们支持某些操作。此时,我们可以使用Trait作为泛型约束。

        在下面的示例代码中,我们首先定义了一个名为Displayable的Trait。然后,我们为Fruit结构体实现了Displayable Trait,并编写了display()方法。接下来,我们编写了一个泛型函数print_all,它接受一个实现了Displayable Trait的类型的切片。最后,我们调用print_all()方法,输出了所有水果的信息。

trait Displayable {fn display(&self);
}struct Fruit {name: String,
}impl Displayable for Fruit {fn display(&self) {println!("Fruit is {}", self.name);}
}fn print_all<T: Displayable>(items: &[T]) {for item in items {item.display();}
}fn main() {let fruits = [Fruit { name: String::from("Lemon") },Fruit { name: String::from("Apple") },Fruit { name: String::from("Peach") },];print_all(&fruits);
}

        另外,如果一个函数接受一个参数,并且要求这个参数必须同时满足多个Trait,可以用+符号来表示。对于一些复杂的实现关系,我们可以使用where关键字简化。

fn do_both_actions<T: Fly + Swim>(animal: T) {animal.fly();animal.swim();
}fn do_both_actions2<T>(animal: T)
whereT: Fly + Swim
{animal.fly();animal.swim();
}fn main() {let duck = Duck { name: "Donald".to_string() };do_both_actions(duck);do_both_actions2(duck);
}

Trait对象

        要使用Trait对象,我们需要先定义Trait。

        在下面的示例代码中,我们首先定义了一个名为Animal的Trait。然后,我们为Dog结构体和Cat结构体实现了Animal Trait,并编写了speak()方法。

trait Animal {fn speak(&self);
}struct Dog;
impl Animal for Dog {fn speak(&self) {println!("Dog");}
}struct Cat;
impl Animal for Cat {fn speak(&self) {println!("Cat");}
}

        到这里,我们可以创建Trait对象了。在Rust中,Trait对象是通过使用&dyn Trait语法来表示的,其中Trait是一个trait的名字。这种表示法允许我们在运行时进行动态分派,即可以在不知道具体类型的情况下调用trait中定义的方法。为了创建一个Trait对象,可以将实现了该trait的具体类型的引用转换为&dyn Trait。

        在下面的示例代码中,我们声明了Dog和Cat的实例,分别为dog和cat。接着,我们将&dog和&cat赋值给Trait对象dog_ref和cat_ref。由于dog_ref和cat_ref现在都是Animal Trait对象,故可以安全地调用它们的speak方法,而无需知道它们实际是Dog还是Cat。

fn animal_speak(animal: &dyn Animal) {animal.speak();
}fn main() {let dog = Dog;let cat = Cat;// 创建Animal Trait对象let dog_ref: &dyn Animal = &dog;let cat_ref: &dyn Animal = &cat;// 输出:Doganimal_speak(dog_ref);// 输出:Catanimal_speak(cat_ref);
}

        另外,我们还可以将Trait对象作为集合的一部分进行存储,并遍历集合调用Trait对象的方法。

fn main() {let dog = Dog;let cat = Cat;// 将Animal Trait对象存储到向量中let animals: Vec<&dyn Animal> = vec![&dog, &cat];for animal in animals {animal.speak();}
}

        注意:使用Trait对象会带来一些运行时开销,因为需要在堆上分配一个额外的结构体来存储类型信息,并且调用方法时需要进行间接调用。因此,在性能敏感的场景中,应该谨慎使用Trait对象。

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

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

相关文章

Spirngboot中文乱码解决方案

在使用springboot的时候,如果我们直接在控制器里面返回中文, 则默认可能会是乱码,因为默认的编码是ISO8859-1, 要解决这个问题, 就需要我们通过重写springboot里面的configureMessageConverters方法来将默认的编码设置为utf-8即可解决, 当然你的类文件编码也必须要是utf-8的, …

leetcode-打家劫舍专题系列(动态规划)

198.打家劫舍 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定一个代表每个…

724.寻找数组的中心下标

题目&#xff1a;给你一个整数数组 nums &#xff0c;请计算数组的 中心下标 。 数组 中心下标 是数组的一个下标&#xff0c;其左侧所有元素相加的和等于右侧所有元素相加的和。 如果中心下标位于数组最左端&#xff0c;那么左侧数之和视为 0 &#xff0c;因为在下标的左侧不…

Golang常用web框架

Gin、beego 和 GoFrame 都是流行的 Go 语言框架&#xff0c;用于构建 web 应用程序。尽管它们都有相似的目标&#xff0c;但它们在设计哲学、性能、配置方式和社区支持等方面有所不同。以下是这三个框架的一些基本区别&#xff1a; Gin&#xff1a; Gin 是一个高性能的 HTTP w…

主流常见视频编辑软件

视频编辑软件是一种用于对数字视频进行非线性剪辑、合成、添加特效、调整色彩、添加字幕等处理的计算机软件。以下是一些常见的视频编辑软件&#xff1a; Adobe Premiere Pro&#xff1a; Adobe Premiere Pro 是一款专业的视频编辑软件&#xff0c;被广泛用于电影、电视剧、广告…

SpringBoot(容器功能)

文章目录 1.Configuration 添加/注入bean1.注入bean1.编写一个JavaBean&#xff0c;Monster.java2.创建一个config文件夹&#xff08;名字任意&#xff09;&#xff0c;用于存放配置Bean的类&#xff08;相当于配置文件&#xff09;3.BeanConfig.java4.测试使用 MainApp.java2.…

Regex正则表达式 --java学习笔记

正则表达式 由一些特定字符组成&#xff0c;代表的是一个规则作用一&#xff1a;校验数据格式是否合法作用二&#xff1a;在一段文本中查找满足要求的内容 String提供了一个匹配正则表达式的方法 public boolean matches&#xff08;String regex&#xff08;正则表达式&…

Delft3D建模、水动力模拟方法及在地表水环境影响评价中的技术应用

​任博士&#xff0c;长期从事地表水数值模拟研究与实践工作&#xff0c;具有资深的技术底蕴和专业背景。 1、掌握Delft3D的建模流程&#xff0c;包括基础数据的准备、计算网格的制作、模型的调试与率定、计算结果的处理等&#xff0c;熟悉软件的基本操作。 2、熟悉Delft3D网…

18个惊艳的可视化大屏(第24辑):通讯行业的应用

实时监控&#xff1a; 可视化大屏可以实时监控通讯网络的运行状态和性能指标。通过可视化展示网络的拓扑结构、设备状态、带宽利用率、延迟等数据&#xff0c;运维人员可以及时发现和解决网络故障&#xff0c;保障通讯网络的稳定性和可靠性。 故障诊断与分析&#xff1a; 可视…

YOLOv8 | 有效涨点,添加GAM注意力机制,使用Wise-IoU有效提升目标检测效果(附报错解决技巧,全网独家)

目录 摘要 基本原理 通道注意力机制 空间注意力机制 GAM代码实现 Wise-IoU WIoU代码实现 yaml文件编写 完整代码分享&#xff08;含多种注意力机制&#xff09; 摘要 人们已经研究了各种注意力机制来提高各种计算机视觉任务的性能。然而&#xff0c;现有方法忽视了…

C语言例3-20:使用逻辑运算符的例子

代码如下&#xff1a; #include<stdio.h> int main(void) {int x3, y100;float f11.0f, f22.1f;char cd; //d(100)printf("!x 的值为&#xff1a; %d\n",!x); //0printf("x||y 的值为&#xff1a; %d\n",x||y); //1print…

ai怎么制作ppt?保姆级的ai一键生成ppt教程来了!

面对市面上多如牛毛的 ai 生成 ppt 软件&#xff0c;哪一款更适合日常使用呢&#xff1f;与此同时&#xff0c;在选定一款 ai 软件后&#xff0c;如何用 ai 制作 ppt&#xff0c;也是很多人第一次使用 pptai 工具会面临的具体问题。 就着这些问题&#xff0c;在接下来的文章中…

有哪些便宜的通配符(泛域名)证书?怎么申请?

通配符&#xff08;泛域名&#xff09;SSL证书就是用来保护一个主域名以及所有二级子域名的证书&#xff0c;相对于单域名证书更具有性价比。 主要优势在于&#xff1a; 一&#xff1a;一个整数覆盖所有子域名 仅仅用一张证书就可以保护一个主域名以及所有子域名&#xff0c;…

[AIGC] Spring Boot的切面编程可以用来解决哪些常见的问题?

Spring Boot切面编程的应用场景 Spring Boot的切面编程&#xff08;AOP&#xff09;有着诸多的应用场景。借助AOP&#xff0c;我们可以对多种类型和对象进行跨切面管理&#xff0c;例如事务管理。以下是一些常见的问题&#xff0c;我们可以使用Spring Boot的AOP来解决。 1. 日…

HPA数据库及HPAanalyze包使用

关于HPA数据库的介绍&#xff1a;Human Protein Atlas 数据库 – 王进的个人网站 (jingege.wang) The Human Protein Atlas 文献 HPAanalyze: an R package that facilitates the retrieval and analysis of the Human Protein Atlas data | BMC Bioinformatics | Full Text …

【JavaScript】requestAnimationFrame 实现表格的流畅滚动

效果 滚动视频demo 实现 // tableRow: 表格滚动的dom const tableRow ref<HTMLElement | null>(null);onMounted(() > {nextTick(() > {scroll();}); });// 自动滚动 const scroll () > {let isScroll true;// 帧动画实现滚动const step () > {if (isSc…

Java面试最强总结(常见面试问题与6条面试经验)

今天分享Java面试完整内容主要包含&#xff1a; 面试前的心态准备(3点建议)技术硬实力包含的范围(50题目)个人简历突出和优化(3点优化步骤)个人软实力的提升(6点提升维度)Java面试经验总结(1点总结) 总结的java面试资料下载地址&#xff1a;java面试资料 1. Java面试前的心态…

【PPO】近端策略优化【Clip版本,离散动作】

本博客代码参考了《动手学强化学习-PPO》 PPO算法是在Actor-Critic的基础上进行训练目标的调整。其改进的地方在于对每次参数更新进行了限制。 PPO 是 TRPO 的一种改进算法&#xff0c;它在实现上简化了 TRPO 中的复杂计算&#xff0c;并且它在实验中的性能大多数情况下会比 …

服务模块划分规范

一、PO :(persistant object )&#xff0c;持久对象 可以看成是与数据库中的表相映射的java对象。使用Hibernate来生成PO是不错的选择。 二、VO :(value object) &#xff0c;值对象 通常用于业务层之间的数据传递&#xff0c;和PO一样也是仅仅包含数据而已。但应是抽象出的…

功能问题:如何用Docker部署一个后端项目?

大家好&#xff0c;我是大澈&#xff01; 本文约1800字&#xff0c;整篇阅读大约需要3分钟。 关注微信公众号&#xff1a;“程序员大澈”&#xff0c;免费加入问答群&#xff0c;一起交流技术难题与未来&#xff01; 现在关注公众号&#xff0c;免费送你 ”前后端入行大礼包…