【Rust自学】10.3. trait Pt.1:trait的定义、约束与实现

喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)

题外话:trait的概念非常非常非常重要!!!整个第10章全都是Rust的重难点!!!
请添加图片描述

10.3.1. 什么是trait

trait意为特征、特质。trait用来向Rust编译器描述某种类型具有哪些并且可以与其它类型共享的功能。trait可以以抽象的方式来定义共享的行为。

与trait相关的还有trait bounds(约束)的概念,它可以将泛型类型参数指定为实现了特定行为的类型。换句话说就是要求泛型的类型参数实现了某些triat。

Rust里的trait与其他语言的接口(interface)有点类似,但还是有区别的。

10.3.2. 定义一个trait

类型的行为由该类型本身可调用的方法来组成。有时候在不同的类型上都具有相同的方法,这时候就称这些类型共享了相同的行为。trait提供了一种方式可以把一些方法放到一起,从而定义实现某种目的所必需的一种行为。

  • 定义trait使用关键字trait,在trait的定义内只有方法签名,没有具体实现
  • trait可以有多个方法,每个方法占一行,以;结尾
  • 实现该trait的类型必须提供具体的方法实现,也就是必须有方法体

看个例子:

pub trait Summary {fn summarize(&self) -> String;
}

trait前面加上pub代表公共的,这个trait的名字是Summary,里面有一个方法的签名叫summerize,除了&self之外没有其他参数,返回类型是String,然后加一个;就结束了这个签名,它没有方法体,也就是没有具体的实现。当然一个trait下可以有很多个方法签名:

pub trait Summary {fn summarize(&self) -> String;fn summarize1(&self) -> String;fn summarize2(&self) -> String;//......
}

10.3.3. 在类型上实现trait

在类型上实现trait与为类型实现方法很类似,但是也有不同之处。

为类型实现方法的写法是impl关键字后面跟着类型就可以了:

impl Yyyy {....}

而在类型上实现trait的写法是:

impl Xxxx for Yyyy {....}
  • Xxxx指的是trait的名
  • Yyyy指的是类型的名
  • 在花括号内需要对trait里的方法签名写下具体的实现

看个例子(lib.rs):

pub trait Summary {fn summarize(&self) -> String;
}pub struct NewsArticle {pub headline: String,pub location: String,pub author: String,pub content: String,
}impl Summary for NewsArticle {fn summarize(&self) -> String {format!("{}, by {} ({})", self.headline, self.author, self.location)}
}pub struct Tweet {pub username: String,pub content: String,pub reply: bool,pub retweet: bool,
}impl Summary for Tweet {fn summarize(&self) -> String {format!("{}: {}", self.username, self.content)}
}
  • 结构体NewsArticle表示新闻文章,它有4个字段:headline标题、location地点、author作者、content内容
  • 结构体Tweet表示推特(现在是X了)推文,它有四个字段:username用户名、content内容、reply是否有回复、retweet是否是转发

这两个结构体类型肯定不同,里面的字段大部分也不同。但是它们都可以有一个同样的行为——提取摘要Summary,所以就分别在这两个类型上实现Summary这个trait。

impl Summary for NewsArticle {fn summarize(&self) -> String {format!("{}, by {} ({})", self.headline, self.author, self.location)}
}

这一段是在NewsArticle实现trait,因为在定义trait时写了summarize的方法签名,所以在这里就得写具体的实现:使用format!这个宏将self.headlineself.authorself.location组成一个字符串返回。

impl Summary for Tweet {fn summarize(&self) -> String {format!("{}: {}", self.username, self.content)}
}

这一段是在Tweet上实现trait,也是一样的写summarize的具体实现:使用format!这个宏将self.usernameself.content组成一个字符串返回。

然后来到main.rs,看它们实例的调用:

use RustStudy::{Summary, Tweet};fn main() {let tweet = Tweet {username: String::from("horse_ebooks"),content: String::from("of course, as you probably already know, people",),reply: false,retweet: false,};println!("1 new tweet: {}", tweet.summarize());
}

记住在main函数里使用之前要先引入作用域,写法是:

use 你的package名::...::你需要的模块

你的package名就是Cargo.toml里的项目名,把它复制下来即可

这里引入Summary是因为使用了在Summary这个trait下的summarize方法;引入Tweet是因为使用了Tweet这个结构体。

看一下输出:

1 new tweet: horse_ebooks: of course, as you probably already know, people

10.3.4. trait的约束

想要在某个类型上实现某个trait的前提条件是:

  • 这个类型(比如说Tweet类型)或这个trait(让Vector实现本地的Summary)是在本地crate里定义的
  • 无法为外部类型实现外部trait。比如说在本地库里标准库Vector实现标准库Display trait。
    这个限制是程序属性的一部分(也就是一致性)。更具体的说是孤儿原则,之所以这么命名是因为它的父类型并没有定义在当前库中。这个规则确保了其他人的代码不能随意破坏你的代码,反之亦然。如果没有这个规则,两个crate可以为同一个类型实现同一个trait,Rust就不知道应该使用哪个实现。

10.3.5. 默认实现

有些时候,为trait中的某些或者是所有方法提供默认行为是非常有用的,它可以使我们无需为每一个类型的实现都提供自定义的行为。我们可以针对某些特定的类型实现trait里的方法。

当我们为某些类型实现trait时,我们可以选择保留或是重载每个方法的默认实现。

之前的写法是:

pub trait Summary {fn summarize(&self) -> String;
}

之前的写法只写了方法的签名,没有写实现,而其实可以给它写一个默认的实现

默认实现:

pub trait Summary {fn summarize(&self) -> String {String::from("(Read more...)")}
}

这里的默认实现就是返回一个字符串"(Read more…)"

由于这个方法在trait里面已经有一个默认实现了,所以在具体的类型上就可以直接采用这个默认实现,而不进行自己的实现。

NewsArticle为例,原本它有自己的实现(或者叫做默认实现的重写的实现):

impl Summary for NewsArticle {fn summarize(&self) -> String {format!("{}, by {} ({})", self.headline, self.author, self.location)}
}

只要删掉这个具体实现就可以让NewsArticle用默认实现:

impl Summary for NewsArticle {}

还有一点需要知道,默认实现的方法可以调用trait中其它的方法,即使这些方法没有默认实现:

pub trait Summary {fn summerize_author(&self) -> Stringfn summarize(&self) -> String {String::from("(Read more from {}...)", self.summerize_author())}
}

summarize的默认实现里调用了summerize_author,即使它只是一个签名,没有具体实现。但如果想要在类型上实现summerize的话就需要先写summerize_author的实现:

impl Summary for NewsArticle {fn summerize_author(&self) -> String {format!("@{}", self.author)}
}

PS:由于NewsArticlesummarize使用的是默认实现,所以就不需要在这里写summerize的默认实现了

这个写法有一点注意:无法从方法的重写实现里面调用默认的实现

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

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

相关文章

大白话拆解——多线程中关于死锁的一切(七)(已完结)

前言: 25年初,这个时候好多小伙伴都在备战期末 小编明天还有一科考试,日更一篇,今天这篇一定会对小白非常有用的!!! 因为我们会把案例到用代码实现的全过程思路呈现出来!&#xff…

GitLab集成Runner详细版--及注意事项汇总【最佳实践】

一、背景 看到网上很多用户提出的runner问题其实实际都不是问题,不过是因为对runner的一些细节不清楚导致了误解。本文不系统性的介绍GitLab-Runner,因为这类文章写得好的特别多,本文只汇总一些常几的问题/注意事项。旨在让新手少弯路。 二、…

《数据结构》期末考试测试题【中】

《数据结构》期末考试测试题【中】 21.循环队列队空的判断条件为?22. 单链表的存储密度比1?23.单链表的那些操作的效率受链表长度的影响?24.顺序表中某元素的地址为?25.m叉树第K层的结点数为?26. 在双向循环链表某节点…

「Mac畅玩鸿蒙与硬件54」UI互动应用篇31 - 滑动解锁屏幕功能

本篇教程将实现滑动解锁屏幕功能,通过 Slider 组件实现滑动操作,学习事件监听、状态更新和交互逻辑的实现方法。 关键词 滑动解锁UI交互状态管理动态更新事件监听 一、功能说明 滑动解锁屏幕功能包含以下功能: 滑动解锁区域:用…

螺栓松动丢失腐蚀生锈检测数据集VOC+YOLO格式504张4类别

数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):504 标注数量(xml文件个数):504 标注数量(txt文件个数):504 标注…

Postman测试big-event

报错500。看弹幕,知道可能是yml或sql有问题。 所以检查idea工作台, 直接找UserMapper检查,发现完全OK。 顺着这个error发现可能是sql有问题。因为提示是sql问题,而且是有now()的那个sql。 之后通过给的课件,复制课件…

如何使用大语言模型进行事件抽取与关系抽取

诸神缄默不语-个人CSDN博文目录 文章目录 1. 什么是事件抽取与关系抽取?2. 示例:使用大语言模型进行事件抽取与关系抽取 1. 什么是事件抽取与关系抽取? 事件抽取是指从文本中识别出与某些“事件”相关的信息。这些事件通常包括动作、参与者、…

NAT网络技术

NAT(Network Address Translation,网络地址转换)是一种常用的网络技术,主要用于在私有网络和公共网络之间转换IP地址。在家庭和小型企业网络当中用的比较多。它的主要功能有IP地址重用和增强网络的安全性。   NAT允许一个整个网…

SpringBoot框架开发中常用的注解

文章目录 接收HTTP请求。RestController全局异常处理器Component依赖注入LombokDataBuildersneakyThrowsRequiredArgsConstructor 读取yml文件配置类注解 接收HTTP请求。 RequestMapping 接收HTTP请求。具体一点是 GetMapping PostMapping PutMapping DeleteMapping 一共…

TVS二极管选型【EMC】

TVS器件并联在电路中,当电路正常工作时,他处于截止状态(高阻态),不影响线路正常工作,当线路处于异常过压并达到其击穿电压时,他迅速由高阻态变为低阻态,给瞬间电流提供一个低阻抗导通…

Azkaban其二,具体使用以及告警设置

目录 Azkaban的使用 1、使用Flow1.0(比较老旧) 2、Flow2.0的用法 1、小试牛刀 2、YAML格式的数据 3、多任务依赖 4、内嵌流(嵌套流)案例 5、动态传参 3、Azkaban的报警机制 1)邮箱通知 2)电话报警机制 4、关…

文档 | Rstudio下的轻量级单页面markdown阅读器 markdownReader

需求:在写R数据分析项目的时候,代码及结果的关键变化怎么记录下来?最好git能很容易的跟踪版本变化。 markdown 是最理想的选择,本文给出一种Rstuidio下的轻量级md阅读器实现:markdownReader。书写md还是在Rstudio。更…

SonarQube相关的maven配置及使用

一、maven 全局配置 <settings><pluginGroups><pluginGroup>org.sonarsource.scanner.maven</pluginGroup></pluginGroups><profiles><profile><id>sonar</id><activation><activeByDefault>true</acti…

Arduino Uno简介与使用方法

目录 一、Arduino Uno概述 1. 硬件特性 2. 开发环境 二、Arduino Uno的基本使用方法 1. 硬件连接 2. 软件编程 三、Arduino Uno编程基础 1. 基本语法 2. 常用函数 四、Arduino Uno应用举例 1. LED闪烁 2. 温度检测 3. 超声波测距 五、Arduino Uno的扩展与应用 1…

UniApp | 从入门到精通:开启全平台开发的大门

UniApp | 从入门到精通:开启全平台开发的大门 一、前言二、Uniapp 基础入门2.1 什么是 Uniapp2.2 开发环境搭建三、Uniapp 核心语法与组件3.1 模板语法3.2 组件使用四、页面路由与导航4.1 路由配置4.2 导航方法五、数据请求与处理5.1 发起请求5.2 数据缓存六、样式与布局6.1 样…

滑动窗口。

1456 定长子串中元音的最大数目 采用滑动窗口。每次移动一个位置&#xff0c;判断当前窗口内的子串内目标元素的个数&#xff0c;若比之前更大就更新结果。 如何判断是否更新结果&#xff1f;也即&#xff0c;如何判断当前窗口内所含目标元素个数&#xff0c;是否为遍历到这个…

公共数据授权运营系统建设手册(附下载)

在全球范围内&#xff0c;许多国家和地区已经开始探索公共数据授权运营的路径和模式。通过建立公共数据平台&#xff0c;推动数据的开放共享&#xff0c;促进数据的创新应用&#xff0c;不仅能够提高政府决策的科学性和公共服务的效率&#xff0c;还能够激发市场活力&#xff0…

电脑主机后置音频插孔无声?还得Realtek高清晰音频管理器调教

0 缘起 一台联想电脑&#xff0c;使用Windows 10 专业版32位&#xff0c;电脑主机后置音频插孔一直没有声音&#xff0c;所以音箱是接在机箱前面版的前置音频插孔上的。 一天不小心捱到了音箱的音频线&#xff0c;音频线头断在音频插孔里面了&#xff0c;前置音频插孔因此用不…

【微服务】1、引入;注册中心;OpenFeign

微服务技术学习引入 - 微服务自2016年起搜索指数持续增长&#xff0c;已成为企业开发大型项目的必备技术&#xff0c;中高级java工程师招聘多要求熟悉微服务相关技术。微服务架构介绍 概念&#xff1a;微服务是一种软件架构风格&#xff0c;以专注于单一职责的多个响应项目为基…

UDP_TCP

目录 1. 回顾端口号2. UDP协议2.1 理解报头2.2 UDP的特点2.3 UDP的缓冲区及注意事项 3. TCP协议3.1 报头3.2 流量控制2.3 数据发送模式3.4 捎带应答3.5 URG && 紧急指针3.6 PSH3.7 RES 1. 回顾端口号 在 TCP/IP 协议中&#xff0c;用 “源IP”&#xff0c; “源端口号”…