【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);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

51单片机——共阴数码管实验

数码管中有8位数字&#xff0c;从右往左分别为LED1、LED2、...、LED8&#xff0c;如下图所示 如何实现点亮单个数字&#xff0c;用下图中的ABC来实现 P2.2管脚控制A&#xff0c;P2.3管脚控制B&#xff0c;P2.4管脚控制C //定义数码管位选管脚 sbit LSAP2^2; sbit LSBP2^3; s…

SwiftUI 撸码常见错误 2 例漫谈

概述 在 SwiftUI 日常撸码过程中&#xff0c;头发尚且还算茂盛的小码农们经常会犯这样那样的错误。虽然犯这些错的原因都很简单&#xff0c;但有时想要快速准确的定位它们却并不容易。 况且这些错误还可能在模拟器和 Xcode 预览&#xff08;Preview&#xff09;表现的行为不甚…

米哈游可切换角色背景动态壁纸

米哈游可切换角色背景动态壁纸 0. 视频 B站演示: 米哈游可切换角色背景动态壁纸-wallpaper 1. 基本信息 作者: 啊是特嗷桃系列: 复刻系列 (衍生 wallpaper壁纸引擎 用)网站: 网页版在线预览 (没有搞大小适配, 建议横屏看; 这个不能切角色, 只能在wallpaper中切)仓库: GitHub…

OWASP ZAP之API 请求基础知识

ZAP API 提供对 ZAP 大部分核心功能的访问,例如主动扫描器和蜘蛛。ZAP API 在守护进程模式和桌面模式下默认启用。如果您使用 ZAP 桌面,则可以通过访问以下屏幕来配置 API: Tools -> Options -> API。 ZAP 需要 API 密钥才能通过 REST API 执行特定操作。必须在所有 …

Elasticsearch: 高级搜索

这里写目录标题 一、match_all匹配所有文档1、介绍&#xff1a; 二、精确匹配1、term单字段精确匹配查询2、terms多字段精确匹配3、range范围查询4、exists是否存在查询5、ids根据一组id查询6、prefix前缀匹配7、wildcard通配符匹配8、fuzzy支持编辑距离的模糊查询9、regexp正则…

齿轮缺陷检测数据集VOC+YOLO格式485张3类别

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

ArkTs之NAPI学习

1.Node-api组成架构 为了应对日常开发经的网络通信、串口访问、多媒体解码、传感器数据收集等模块&#xff0c;这些模块大多数是使用c接口实现的&#xff0c;arkts侧如果想使用这些能力&#xff0c;就需要使用node-api这样一套接口去桥接c代码。Node-api整体的架构图如下&…

MySQL(五)MySQL图形化工具-Navicat

1. MySQL图形化工具-Navicat Navicat是一套快速、可靠的数据库管理工具&#xff0c;Navicat是以直觉化的图形用户界面而建的&#xff0c;可以兼容多种数据库&#xff0c;支持多种操作系统。   Navicat for MySQL是一款强大的 MySQL 数据库管理和开发工具&#xff0c;它为专业…

【OceanBase】通过 OceanBase 的向量检索技术构建图搜图应用

文章目录 一、向量检索概述1.1 关键概念① 非结构化数据② 向量③ 向量嵌入(Embedding)④ 向量相似性检索 1.2 应用场景 二、向量检索核心功能三、图搜图架构四、操作步骤4.1 使用 Docker 部署 OceanBase 数据库4.2 测试OceanBase数据库连通性4.3 开启数据库向量检索功能4.4 克…

微信流量主挑战:用户破16!新增文档转换(新纪元3)

朋友们&#xff0c;报告好消息&#xff01;我的小程序用户数量已经涨到16个了&#xff01;没错&#xff0c;真没拉朋友圈亲戚好友来撑场子&#xff0c;全靠实力&#xff08;和一点点运气&#xff09;吸引了16位陌生小伙伴光临&#xff01;这波进步&#xff0c;连我自己都感动了…

Python:交互式物质三态知识讲解小工具

学着物理写着Python 以下是一个使用Python的Tkinter库实现的简单示例程序&#xff0c;通过图形界面展示并讲解固态、液态、气态的一些特点&#xff0c;代码中有详细的注释来帮助你理解各部分功能&#xff1a; 完整代码 import tkinter as tk from tkinter import ttk import …

ESP32-S3遇见OpenAI:OpenAI官方发布ESP32嵌入式实时RTC SDK

目录 OpenAI RTC SDK简介应用场景详解智能家居控制系统个人健康助手教育玩具 技术亮点解析低功耗设计快速响应高精度RTC安全性保障开发者指南 最近&#xff0c;OpenAI官方发布了一款针对ESP32-S3的嵌入式实时RTC&#xff08;实时时钟&#xff09;SDK&#xff0c;这标志着ESP32-…

Windows 11 关闭 VBS(基于虚拟化的安全性)

注&#xff1a;本文为 “Windows 11 关闭 VBS” 相关方法文章合辑。 重传部分 csdn 转储异常图片&#xff0c;未整理去重。 Win11 关闭 VBS 的几种方法 适用机型&#xff1a;台式 / ThinkCentre / 笔记本 / ThinkPad 分析 Virtualization-based Security (VBS) 基于虚拟化的…

小程序租赁系统的优势与应用探索

内容概要 小程序租赁系统&#xff0c;听起来很高大上&#xff0c;但实际上它比你想象的要实用得多&#xff01;设想一下&#xff0c;几乎所有的租赁需求都能通过手机轻松解决。这种系统的便捷性体现在让用户随时随地都能发起租赁请求&#xff0c;而不再受制于传统繁琐的手续。…

简历_专业技能_熟悉Redis常用数据结构及其操作命令

系列博客目录 文章目录 系列博客目录1.Redis通用命令2.String类型3.Hash类型4.List类型5.Set类型6.Sorted类型7.StringRedisTemplate 1.Redis通用命令 通用指令是部分数据类型的&#xff0c;都可以使用的指令&#xff0c;常见的有&#xff1a; KEYS&#xff1a;查看符合模板的…

USB 驱动开发 --- Gadget 设备连接 Windows 免驱

环境信息 测试使用 DuoS(Arm CA53&#xff0c; Linux 5.10) 搭建方案验证环境&#xff0c;使用 USB sniff Wirekshark 抓包分析&#xff0c;合照如下&#xff1a; 注&#xff1a;左侧图中设备&#xff1a;1. 蓝色&#xff0c;USB sniff 非侵入工 USB 抓包工具&#xff1b;2. …

2025年1月4日蜻蜓q旗舰版st完整开源·包含前后端所有源文件·开源可商用可二开·优雅草科技·优雅草kir|优雅草星星|优雅草银满|优雅草undefined

2025年1月4日蜻蜓q旗舰版st完整开源包含前后端所有源文件开源可商用可二开优雅草科技优雅草kir|优雅草星星|优雅草银满|优雅草undefined 产品介绍&#xff1a; 本产品主要贡献者优雅草科技优雅草kir|优雅草星星|优雅草银满|优雅草undefined-青史留名&#xff0c;时光如川浪淘…

【文献精读笔记】Explainability for Large Language Models: A Survey (大语言模型的可解释性综述)(三)

****非斜体正文为原文献内容&#xff08;也包含笔者的补充&#xff09;&#xff0c;灰色块中是对文章细节的进一步详细解释&#xff01; 3.2 全局解释&#xff08;Global Explanation&#xff09; 与旨在解释模型个体预测的局部解释不同&#xff0c;全局解释提供了对语言模型…

体验谷歌最新Gemini 2.0 Flash原生多模态音视频对话桌面分享功能

Gemini 2.0是谷歌最新推出的原生多模态输入输出的AI模型。Gemini 2.0 Flash是2.0家族第一个模型&#xff0c;以多模态输入输出和Agent技术为核心&#xff0c;速度比 1.5 Pro快两倍&#xff0c;关键性能指标超过 1.5 Pro。模型支持原生工具调用和实时音视频流输入&#xff0c;提…

Leecode刷题C语言之我的日程安排表③

执行结果:通过 执行用时和内存消耗如下&#xff1a; typedef struct {int size;int maxIntersection;int** books;// #ifdef DEBUG// int runCount;// #endif } MyCalendarThree;void insert(MyCalendarThree*, int, int, int, int); int* binarySearch(int*, int, int);MyCal…