Rust编程的泛型

news2024/10/12 14:07:34

【图书介绍】《Rust编程与项目实战》-CSDN博客

《Rust编程与项目实战》(朱文伟,李建英)【摘要 书评 试读】- 京东图书 (jd.com)

Rust编程与项目实战_夏天又到了的博客-CSDN博客

7.6.1  什么是泛型编程

C/C++、Rust都是强类型语言,在对数据进行处理时,必须明确数据的数据类型。但是很多时候,比如链表这种数据结构,可以是整型数据的链表,也可以是其他类型,可能会写出重复的代码,只是数据类型不同而已。泛型编程是一种编程风格,其中的算法以尽可能抽象的方式编写,而不依赖于将在其上执行这些算法的数据形式。泛型这个词并不是通用的,在不同的语言实现中,具有不同的命名。在Java/Kotlin/C#中称为泛型,在ML/Scala/Haskell中称为Parametric Polymorphism,而在C++中被叫作模板,比如最负盛名的C++中的STL。任何编程方法的发展一定有其目的,泛型也不例外。泛型的主要目的是加强类型安全和减少强制转换的次数。

所以,为了简化代码,我们将类型抽象成一种“参数”,数据和算法针对这种抽象的类型来实现,而不是具体的类型,当需要使用时再具体化、实例化。

7.6.2  在函数中使用泛型

泛型可以在函数中使用,将泛型放在函数的签名中,在其中指定参数的数据类型和返回值。当函数包含类型为T的单个参数时,语法如下:

fn function_name<T>(x:T)  

// body of the function.

上面的语法包含两部分:

  • <T>:给定的函数是一种类型的泛型。
  • (x : T):x是类型T。

当函数包含多个相同类型的参数时,代码如下:

fn function_name<T>(x:T, y:T)  

// body of the function.

当函数包含多个类型的参数时,代码如下:

fn function_name<T,U>(x:T, y:U) 

// Body of the function.

下面举一个泛型的例子:

//不使用泛型
//针对整型数据
fn findmax_int(list : &[i32]) -> i32 {
    let mut max_int = list[0];
    for &i in list.iter() {
        if i > max_int {
            max_int = i;
        }
    }
    max_int
}

//针对char数据
fn findmax_char(list : &[char]) -> char {
    let mut max_char = list[0];
    for &i in list.iter() {
        if i > max_char {
            max_char = i;
        }
    }
    max_char
}
fn main() {
    let v_int = vec![2, 4, 1, 5, 7, 3];
    println!("max_int: {}", findmax_int(&v_int));
    let v_char = vec!['A', 'C', 'G', 'B', 'F'];
    println!("max_char: {}", findmax_char(&v_char));
}

运行结果如下:

max_int: 7

max_char: G

可以看到两个函数基本上是一样的。下面采用泛型的方式来简化代码:

fn find_max<T : PartialOrd + Copy> (list : &[T]) -> T {
    let mut max = list[0];
    for &i in list.iter() {
        if i > max {
            max = i;
        }
    }
    max
}

fn main() {
    let v_int = vec![2, 4, 1, 5, 7, 3];
    println!("max_int: {}", find_max(&v_int));
    let v_char = vec!['A', 'C', 'G', 'B', 'F'];
    println!("max_char: {}", find_max(&v_char));
}

成功运行,运行结果如下:

max_int: 7

max_char: G

其实泛型是一个比较复杂的概念,可能大家还没体会到泛型的好处,我们再来看一个实际开发中经常会碰到的场景。

话说项目经理总是善变的,有一天项目经理告诉我,替客户计算一个圆形的面积。客户要求很简单,半径只会是u8类型。我写了代码如下:

fn area_u8(r: u8) -> u8 {
    r * r
}

fn main() {
    println!("{}", area_u8(3));
}

第二天项目经理又来了,说客户说的不对,半径在某种情况下还会是u16类型。于是我又添加了一个函数:

fn area_u8(r: u8) -> u8 {
    r * r
}

fn area_u16(r: u16) -> u16 {
    r * r
}

fn main() {
    println!("{}", area_u8(3));
    println!("{}", area_u16(10));
}

但第三天、第四天,项目经理又跑来说半径还会是u32、u64类型,甚至还可能是浮点数。我到底要写多少个函数才行!我意识到是时候叫出“超级飞侠”了。不对,是泛型了。泛型,顾名思义,就是广泛的类型,在Rust中,通常使用<T>表示,当然,不一定是T,也可以是A、B、C……

使用泛型并不容易,在这个例子中,我感受到了Rust编译器的强大。我的第一版程序如下:

fn area<T>(r: T) -> T {
    r * r
}

fn main() {
    println!("{}", area(3));
    println!("{}", area(3.2));
}

然后编译器告诉我:

error[E0369]: cannot multiply `T` by `T`
 --> main.rs:2:7
  |
2 |     r * r
  |     - ^ - T
  |     |
  |     T
  |
help: consider restricting type parameter `T`
  |
1 | fn area<T: std::ops::Mul<Output = T>>(r: T) -> T {
  |          +++++++++++++++++++++++++++

error: aborting due to previous error

不能对两个T类型的数做乘法!那我该怎么办?幸亏有泛型的特性与特性绑定。我这样修改:

fn area<T: std::ops::Mul<Output = T> + Copy>(r: T) -> T {
    r * r
}

fn main() {
    println!("{}", area(3));
    println!("{}", area(3.2));
}

终于可以得到正确结果了,运行结果如下:

9

10.240000000000002

回过头来解释一下刚才的过程。泛型指定的是任意类型,但并不是所有类型都能进行乘法运算。因此,我们需要对泛型加以限制。这被称为特性绑定,或泛型约束,意思是只有满足条件(实现了某个特性)的泛型才被允许传到函数中来。

上面的写法无疑使得第一行很长,可读性不好,为此Rust设计了where子句,用来实现泛型    约束:

fn area<T>(r: T) -> T 
where T: std::ops::Mul<Output = T> + Copy
{
    r * r
}

fn main() {
    println!("{}", area(3));
    println!("{}", area(3.2));
}

运行结果如下:

9

10.240000000000002

7.6.3  在结构体中使用泛型

这下足足过了1个月,我都没见到项目经理的身影,直到有一天,项目经理笑意满满地来到我的工位,说上次的程序写得太棒了,客户发现不管什么时候,我的程序都能正常工作。客户对我们公司非常肯定,决定再给我们一个新的项目:计算长方形的面积,此类项目前景非常好,为了便于扩展,最好能抽象成结构体。于是我一气呵成:

use std::ops::Mul;  				// 这么写可以简化代码

struct Rect<T>      				// 为结构体添加泛型
{
    width: T,       				// 宽和高都是泛型
    height: T
}

impl<T> Rect<T> {   				// 为泛型实现方法,impl后也要添加<T>
    fn area(&self) -> T     
    where T: Mul<Output = T> + Copy { 			// 泛型约束
        self.height * self.width
    }
}

fn main() {
    // 整型
    let rect1 = Rect{width:3, height:4};
    println!("{}", rect1.area());

    // 浮点型
    let rect2 = Rect{width:3.5, height:4.3};
    println!("{}", rect2.area());
}

运行结果如下:

12

15.049999999999999

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

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

相关文章

详解前端开发都需要掌握的十个 JavaScript 基本数组函数

假设你正在开发一个复杂的 Web 项目。你的数据来自许多 API&#xff0c;你的工作是高效地处理、过滤和分析这些数据。你的时间很紧张&#xff0c;所以每一行代码都很重要。 这时学习高级 JavaScript 数组方法就会对你有所帮助。 这些函数不仅可以减少代码量&#xff0c;还可以…

C语言—双链表

一、双向链表的结构 注意&#xff1a;这⾥的“带头”跟前⾯我们说的“头节点”是两个概念&#xff0c;实际前⾯在单链表阶段称呼不严谨&#xff0c;带头链表⾥的头节点&#xff0c;实际为“哨兵位”&#xff0c;哨兵位节点不存储任何有效元素&#xff0c;只是站在这⾥“放哨的”…

desmos和webgl绘制线条

目录 desmos绘制 webgl绘制 将线段坐标生成三角化坐标 处理斜接线段 处理圆角 尖角 先在desmos上面完成线条lineJoin绘制的,再将代码和公式转到js用webgl绘制. desmos绘制 示例 desmos计角斜接角时&#xff0c;需要用到的一些函数。在desmos定义成公共函数&#xff0c…

[含文档+PPT+源码等]精品基于springboot实现的原生Andriod心理健康辅导平台

基于Spring Boot实现的原生Android心理健康辅导平台&#xff0c;其背景可以从以下几个方面进行详细阐述&#xff1a; 一、技术背景 Spring Boot框架&#xff1a;Spring Boot是Spring框架的一个子集&#xff0c;它通过自动配置、简化依赖管理、内嵌容器等特性&#xff0c;极大…

登录前端笔记(一):pinia管理用户数据

一、把pinia加入到项目文件里 ①、npm install pinia ②mian.ts //①导入createPinia import { createPinia } from pinia //②执行方法得到实例 const pinia createPinia() //③把pinia实例加入到APP应用里 app.use(pinia)二、官网简单实例&#xff08;定义store【state与ac…

Stylized Far East 古代国风建筑城镇宫殿场景模型

古代国风建筑城镇宫殿场景模型。内容: -演示场景(截图) - 种类繁多的建筑,如宫殿、商店、神社、房屋、餐馆、宝塔、寺庙等 -带有塔楼、门楼的模块化城堡墙 -树木、岩石、悬崖和其他自然资产 -传统装饰,如纸灯笼、绘画、瓷器等 - 城镇道具,如手推车、栅栏、板条箱、市场、…

Redis-缓存一致性

缓存双写一致性 更新策略探讨 面试题 缓存设计要求 缓存分类&#xff1a; 只读缓存&#xff1a;&#xff08;脚本批量写入&#xff0c;canal 等&#xff09;读写缓存 同步直写&#xff1a;vip数据等即时数据异步缓写&#xff1a;允许延时&#xff08;仓库&#xff0c;物流&a…

el-carousel-item自动重复渲染,使用nanoid让重复的元素包含不同的id

<template><div class"page-container"><div class"m-title">轮播图</div><el-carousel height"400px" :autoplay"true"><el-carousel-item v-for"(item, index) in carouselList" :key&…

AOT漫谈专题(第二篇): 如何对C# AOT轻量级APM监控

一&#xff1a;背景 1. 讲故事 上一篇我们聊到了如何调试.NET Native AOT 程序&#xff0c;这是研究一个未知领域知识的入口&#xff0c;这篇我们再来看下如何对 Native AOT 程序进行轻量级的APM监控&#xff0c;当然这里的轻量级更多的是对 AOT 中的coreclr内容的挖掘。 二…

面试官最喜欢问的28道ZooKeeper面试题

前言 ZooKeeper 是一个分布式的&#xff0c;开放源码的分布式应用程序协调服务。它是一个为分布式应用提供一致性服务的软件&#xff0c;提供的功能包括&#xff1a;配置维护、域名服务、分布式同步、组服务等。 ZooKeeper 的目标就是封装好复杂易出错的关键服务&#xff0c;…

再也不怕面试官问我几百亿ip相关的问题了

首先要明确这一类的问题都是海量那个数据类型的问题&#xff0c;对于海量数据我们一般采用分而治之的思路去解决&#xff0c;考官考察的就是你有没有处理海量数据的经验。总结几个常见的海量数据相关的面试&#xff0c;供参考。 有一个存放10GB的ip地址文件&#xff0c;每行一…

2024年【安全生产监管人员】免费试题及安全生产监管人员模拟试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年安全生产监管人员免费试题为正在备考安全生产监管人员操作证的学员准备的理论考试专题&#xff0c;每个月更新的安全生产监管人员模拟试题祝您顺利通过安全生产监管人员考试。 1、【单选题】()以上地方人民政府…

基于SSM的民宿预订系统的设计与实现

文未可获取一份本项目的java源码和数据库参考。 一、毕业设计&#xff08;论文&#xff09;选题的目的和意义 由于现代人经济水平的不断提高&#xff0c;出门旅游已经成为人们放松和休息的一种生活方式。而出门在外&#xff0c;住宿也是首要问题&#xff0c;相比于传统的酒店…

五子棋项目自动化测试

目录 一、五子棋项目介绍 二、编写Web测试用例 三、自动化测试脚本开发 1、引入依赖 2、设计框架 3、Utils 4、LoginPage 5、RegisterPage 6、MatchPage 7、RunTest类 8、运行程序 一、五子棋项目介绍 五子棋项目是基于 WebSocket 实现的多人在线对战系统&#xff0…

【优选算法】(第三十六篇)

目录 ⼆叉树的锯⻮形层序遍历&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 ⼆叉树的最⼤宽度&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 ⼆叉树的锯⻮形层序遍历&#xff08;medium&#xff09; 题目解析 1.题目链接&#xf…

【高中生讲机器学习】21. 隐马尔可夫模型好难?看过来!(下篇)

创建时间&#xff1a;2024-10-09 首发时间&#xff1a;2024-10-12 最后编辑时间&#xff1a;2024-10-12 作者&#xff1a;Geeker_LStar 你好呀~这里是 Geeker_LStar 的人工智能学习专栏&#xff0c;很高兴遇见你~ 我是 Geeker_LStar&#xff0c;一名高一学生&#xff0c;热爱计…

SpringBoot购物推荐网站:设计与实现的最佳实践

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常适…

『深度分析』Kimi版o1来了!Kimi探索版全面解读!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;专注于分享AI全维度知识&#xff0c;包括但不限于AI科普&#xff0c;AI工…

28幅高清修复的英文版中国地图

我们在《183幅值得珍藏的全国地质图集》和《55幅值得珍藏的水文地质图集》两文中&#xff0c;为你分享过精美的全国地质图和水文地质图。 现在我们整理了28幅英文版中国地图&#xff0c;并经过高清修复后分享给大家&#xff0c;你可以在文末查看该数据的领取方法。 28幅英文版…

Channel模块 --- 管理套接字事件

目录 设计思想 实现 设计思想 Channel模块是用于对一个描述符所需要监控的事件以及事件触发之后要执行的回调函数进行管理的 具体来说&#xff0c;它里面会保存该文件描述符所监控的事件&#xff0c;该文件描述符所就绪的事件&#xff0c;以及该描述符的各种事件的处理回调…