【Rust自学】10.2. 泛型

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

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

10.2.1. 什么是泛型

泛型的主要功能是提高代码的复用能力,适用于处理重复代码的问题,也可以说是实现数据与算法的分离

泛型是具体类型或其它属性的抽象代替。 它的意思是你写代码时写的泛型代码并不是最终的代码,而是一种模版,里面有一些“占位符”。

编译器在编译时会把“占位符”替换为具体的类型。 还是看个例子:

fn largest<T>(list:&[T]) -> T {
//......
}

这个函数的定义就使用了泛型类型参数,这个T就是所谓的“占位符”,写代码时这个T可以是任意的数据类型,但是在编译时编译器会根据具体的使用把T替换为具体的类型,这个过程叫单态化

这个T叫做泛型的类型参数。其实可以使用任意合法的标识符来作为它的类型参数的名,但是按惯例通常是使用大写的T(代表Type)。其实在选择泛型类型参数名的时候,它的名称是很短的,通常一个字母就够了。如果你实在要写长一点,使用驼峰命名规范即可。

10.2.2. 函数定义中的泛型

当使用泛型来定义一个函数的时候,需要将泛型的类型参数放在函数的签名里。而泛型的类型参数通常是用于指定参数和返回的类型。

以上一篇文章的代码为例,使用泛型稍作修改:

fn largest<T>(list: &[T]) -> T{  let mut largest = list[0];  for &item in list{  if item > largest{  largest = item;  }  }  largest  
}

整个函数的定义可以这么理解:函数largest拥有泛型的类型参数T,它接收切片作为参数,切片内的元素为T,而这个元素返回值的类型也是T

尝试编译一下,输出:

error[E0369]: binary operation `>` cannot be applied to type `T`--> src/main.rs:4:17|
4 |         if item > largest{|            ---- ^ ------- T|            ||            T|
help: consider restricting type parameter `T`|
1 | fn largest<T: std::cmp::PartialOrd>(list: &[T]) -> T{|             ++++++++++++++++++++++

这里先不讲原因和修改的方法,只需要知道使用泛型参数大概是这么个写法就对了。后面的文章会讲如何指定特定的trait。

10.2.3. struct定义中的泛型

结构体里定义的泛型类型参数主要是用在它的字段里,看个例子:

struct Point<T> {   x: T,  y: T,  
}  fn main() {  let integer = Point { x: 5, y: 10 };  let float = Point { x: 1.0, y: 4.0 };  
}

在结构体的名字后面呢加上<>,在里面写泛型参数的名称,而这个泛型类型就可以应用于这个结构体下的每个字段。

main函数里实现了这个结构体的实例化,integer里的两个字段是两个i32float里的两个字段是两个f64,因为结构体在声明时xy的类型都是T,所以实例化的xy的类型也得是一个类型,两者的类型得保持一致。

那如果我想要xy是两种不同的类型呢?很简单,声明两个泛型类型就可以:

struct Point<T, U> {   x: T,  y: U,  
}  fn main() {  let integer = Point { x: 5, y: 1.0 };  let float = Point { x: 1.0, y: 40 };  
}

这个时候实例化的xy就可以是不同的类型,当然也可以是一样的类型

需要注意的是,虽然可以使用多个泛型类型参数,但是,太多的泛型会使得可读性下降,通常这意味着代码需要重组为更多的小单元。

10.2.4. enum定义中的泛型

和结构体差不多,枚举中使用泛型类型参数主要是用在变体中华,可以让枚举的变体持有泛型数据类型,比如说最常见的Option<T>Result<T, E>

看个例子:

enum Option<T> {  Some(T),  None,  
}  enum Result<T, E> {  Ok(T),  Err(E),  
}
  • Option枚举中Some(T)也就是Some这个变体持有T类型的值,而None这个变体表示不持有任何值。而正是因为Option枚举使用了泛型,所以无论这个可能存在的值是什么类型的,都可以使用Option<T>来表示
  • 同样的,枚举的类型参数也可以使用多个泛型类型参数,比如说Result这个枚举就使用了TE,在变体Ok里存储的是T,Err存储的是E

10.2.5. 在方法定义中的泛型

方法可以附在枚举或是结构体上,既然枚举和结构体都可以使用泛型参数,那方法自然也可以,如下例:

struct Point<T> {   x: T,  y: T,  
}  impl<T> Point<T> {  fn x(&self) -> &T {  &self.x  }  
}

方法x相当于一个getter,而针对Poinnt<T>这个结构来实现方法的时候需要在impl关键字的后面加上<T>。这样写就表示它是针对泛型T而不是针对某个具体的类型来实现的。

当然,如果是根据具体的类型来实现方法就不需要了:

impl Point<i32> {  fn x1(&self) -> &i32 {  &self.x  }  
}

x1这个方法就只有在Point<i32>这个具体的类型上才有,而其他Point<T>的类型就没有这个方法,类比C++的特化和偏特化。

还有一点需要注意,结构体里的泛型类型参数可以和方法的泛型类型参数不同。看个例子:

struct Point<T, U> {   x: T,  y: U,  
}  impl<T, U> Point<T, U> {  fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {  Point {  x: self.x,  y: other.y,  }  }  
}  fn main() {  let p1 = Point { x: 5, y: 10.4 };  let p2 = Point { x: "Hello", y: 'c' };  let p3 = p1.mixup(p2);  println!("p3.x = {}, p3.y = {}", p3.x, p3.y);  
}

针对Point<T, U>实现了方法mixupmixup有两个泛型类型参数,一个叫V,一个叫W,方法的两个类型参数和Point的两个类型参数是不一样的,当然具类型也有可能是一样的。mixup的第二个参数是other,它的类型也是Point,但这个Point不一定和self所指向的Point的数据类型是一样的,所以需要另起2个新的泛型类型参数。再看看返回类型,是Point<T, W>T来自Point<T, U>W来自Point<V, W>

看下main函数,首先声明了p1,它的两个字段都是i32;然后又声明了p2,它的两个字段分别是&str(字符串切片)和char(用''代表是单个字符)。接着使用了mixup这个函数,p1对应的是Point<T, U>p2对应的是Point<V, W>,又根据各自的字段的类型可以推断出Ti32Ui32VStringWcharmixup返回类型是Point<T, W>,具体到这个例子中就是Point<i32, char>

输出:

p3.x = 5, p3.y = c

10.2.6. 泛型代码的性能

使用泛型的代码和使用具体类型的代码的运行速度是一样的。Rust在编译时会执行单态化,将泛型类型替换为具体的类型,这样在执行的时候就省去了类型替换的过程。

举个例子:

fn main() {let integer = Some(5);let float = Some(5.0)
}

这里integerOption<i32>floatOption<f64>,在编译的时候编译器会把Option<T>展开为Option_i32Option_f64

enum Option_i32 {Some(i32),None,
}enum Option_f64 {Some(f64),None,
}

也就是把Option<T>这个泛型定义替换为了两个具体类型的定义。

单态后的main函数也变成了这样:

enum Option_i32 {Some(i32),None,
}enum Option_f64 {Some(f64),None,
}fn main(){let integer = Option_i32::Some(5);let float = Option_f64::Some(5.0);
}

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

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

相关文章

Spark-Streaming有状态计算

一、上下文 《Spark-Streaming初识》中的NetworkWordCount示例只能统计每个微批下的单词的数量&#xff0c;那么如何才能统计从开始加载数据到当下的所有数量呢&#xff1f;下面我们就来通过官方例子学习下Spark-Streaming有状态计算。 二、官方例子 所属包&#xff1a;org.…

Python 3 输入与输出指南

文章目录 1. 输入与 input()示例&#xff1a;提示&#xff1a; 2. 输出与 print()基本用法&#xff1a;格式化输出&#xff1a;使用 f-string&#xff08;推荐&#xff09;&#xff1a;使用 str.format()&#xff1a;使用占位符&#xff1a; print() 的关键参数&#xff1a; 3.…

【SQLi_Labs】Basic Challenges

什么是人生&#xff1f;人生就是永不休止的奋斗&#xff01; Less-1 尝试添加’注入&#xff0c;发现报错 这里我们就可以直接发现报错的地方&#xff0c;直接将后面注释&#xff0c;然后使用 1’ order by 3%23 //得到列数为3 //这里用-1是为了查询一个不存在的id,好让第一…

时间序列预测算法---LSTM

目录 一、前言1.1、深度学习时间序列一般是几维数据&#xff1f;每个维度的名字是什么&#xff1f;通常代表什么含义&#xff1f;1.2、为什么机器学习/深度学习算法无法处理时间序列数据?1.3、RNN(循环神经网络)处理时间序列数据的思路&#xff1f;1.4、RNN存在哪些问题? 二、…

leetcode题目(3)

目录 1.加一 2.二进制求和 3.x的平方根 4.爬楼梯 5.颜色分类 6.二叉树的中序遍历 1.加一 https://leetcode.cn/problems/plus-one/ class Solution { public:vector<int> plusOne(vector<int>& digits) {int n digits.size();for(int i n -1;i>0;-…

快速上手LangChain(三)构建检索增强生成(RAG)应用

文章目录 快速上手LangChain(三)构建检索增强生成(RAG)应用概述索引阿里嵌入模型 Embedding检索和生成RAG应用(demo:根据我的博客主页,分析一下我的技术栈)快速上手LangChain(三)构建检索增强生成(RAG)应用 langchain官方文档:https://python.langchain.ac.cn/do…

[cg] android studio 无法调试cpp问题

折腾了好久&#xff0c;native cpp库无法调试问题&#xff0c;原因 下面的Deploy 需要选Apk from app bundle!! 另外就是指定Debug type为Dual&#xff0c;并在Symbol Directories 指定native cpp的so路径 UE项目调试&#xff1a; 使用Android Studio调试虚幻引擎Android项目…

【Windows】powershell 设置执行策略(Execution Policy)禁止了脚本的运行

报错信息&#xff1a; 无法加载文件 C:\Users\11726\Documents\WindowsPowerShell\profile.ps1&#xff0c;因为在此系统上禁止运行脚本。有关详细信息&#xff0c;请参 阅 https:/go.microsoft.com/fwlink/?LinkID135170 中的 about_Execution_Policies。 所在位置 行:1 字符…

可编辑37页PPT |“数据湖”构建汽车集团数据中台

荐言分享&#xff1a;随着汽车行业智能化、网联化的快速发展&#xff0c;数据已成为车企经营决策、优化生产、整合供应链的核心资源。为了在激烈的市场竞争中占据先机&#xff0c;汽车集团亟需构建一个高效、可扩展的数据管理平台&#xff0c;以实现对海量数据的收集、存储、处…

【快速实践】类激活图(CAM,class activation map)可视化

类激活图可视化&#xff1a;有助于了解一张图像的哪一部分让卷积神经网络做出了最终的分类决策 对输入图像生成类激活热力图类激活热力图是与特定输出类别相关的二维分数网格&#xff1a;对任何输入图像的每个位置都要进行计算&#xff0c;它表示每个位置对该类别的重要程度 我…

ros2 py文件间函数调用

文章目录 写在前面的话生成python工程包命令运行python函数命令python工程包的目录结构目录结构&#xff08;细节&#xff09; 报错 1&#xff08; no module name ***&#xff09;错误示意 截图终端输出解决方法 报错 2&#xff08; AttributeError: *** object has no attrib…

Milvus×合邦电力:向量数据库如何提升15%电价预测精度

01. 全球能源市场化改革下的合邦电力 在全球能源转型和市场化改革的大背景下&#xff0c;电力交易市场正逐渐成为优化资源配置、提升系统效率的关键平台。电力交易通过市场化手段&#xff0c;促进了电力资源的有效分配&#xff0c;为电力行业的可持续发展提供了动力。 合邦电力…

OLED的显示

一、I2C I2C时序&#xff1a;时钟线SCL高电平下&#xff1a;SDA由高变低代表启动信号&#xff0c;开始发送数据&#xff1b;SCL高电平时&#xff0c;数据稳定&#xff0c;数据可以被读走&#xff0c;开始进行读操作&#xff0c;SCL低电平时&#xff0c;数据发生改变&#xff1…

VMware运维效率提升50%,RVTools管理更简单

RVTools 是一款专为 VMware 虚拟化环境量身打造的高效管理工具&#xff0c;基于 .NET 4.7.2 框架开发&#xff0c;并与 VMware vSphere Management SDK 8.0 和 CIS REST API 深度集成&#xff0c;能够全面呈现虚拟化平台的各项关键数据。该工具不仅能够详细列出虚拟机、CPU、内…

python +t kinter绘制彩虹和云朵

python t kinter绘制彩虹和云朵 彩虹&#xff0c;简称虹&#xff0c;是气象中的一种光学现象&#xff0c;当太阳光照射到半空中的水滴&#xff0c;光线被折射及反射&#xff0c;在天空上形成拱形的七彩光谱&#xff0c;由外圈至内圈呈红、橙、黄、绿、蓝、靛、紫七种颜色。事实…

Zabbix5.0版本(监控Nginx+PHP服务状态信息)

目录 1.监控Nginx服务状态信息 &#xff08;1&#xff09;通过Nginx监控模块&#xff0c;监控Nginx的7种状态 &#xff08;2&#xff09;开启Nginx状态模块 &#xff08;3&#xff09;配置监控项 &#xff08;4&#xff09;创建模板 &#xff08;5&#xff09;用默认键值…

Python入门教程 —— 字符串

字符串介绍 字符串可以理解为一段普通的文本内容,在python里,使用引号来表示一个字符串,不同的引号表示的效果会有区别。 字符串表示方式 a = "Im Tom" # 一对双引号 b = Tom said:"I am Tom" # 一对单引号c = Tom said:"I\m Tom" # 转义…

AcWing练习题:差

读取四个整数 A,B,C,D&#xff0c;并计算 (AB−CD)的值。 输入格式 输入共四行&#xff0c;第一行包含整数 A&#xff0c;第二行包含整数 B&#xff0c;第三行包含整数 C&#xff0c;第四行包含整数 D。 输出格式 输出格式为 DIFERENCA X&#xff0c;其中 X 为 (AB−CD) 的…

小程序添加购物车业务逻辑

数据库设计 DTO设计 实现步骤 1 判断当前加入购物车中的的商品是否已经存在了 2 如果已经存在 只需要将数量加一 3 如果不存在 插入一条购物车数据 4 判断加到本次购物车的是菜品还是套餐 Impl代码实现 Service public class ShoppingCartServiceImpl implements Shoppin…

如何在谷歌浏览器中使用自定义搜索快捷方式

在数字时代&#xff0c;浏览器已经成为我们日常生活中不可或缺的一部分。作为最常用的浏览器之一&#xff0c;谷歌浏览器凭借其简洁的界面和强大的功能深受用户喜爱。本文将详细介绍如何自定义谷歌浏览器的快捷工具栏&#xff0c;帮助你更高效地使用这一工具。 一、如何找到谷歌…