【第九课】Rust中泛型和特质

目录

前言

泛型

单态化

特质

多态

总结


前言

这节课来介绍一下Rust中的泛型和特质,泛型在大部分语言中都有,和Java中的泛型差不多,特质在Java并没有,但是在Scala中有,特质其实类似于Java中的接口Interface。

泛型

(1)容器中的泛型

Rust中我们使用的Vector、HashMap其实都是有泛型的,和其他编程语言中的泛型一样

(2)结构体中的泛型

下面代码展示了结构体中的泛型,包括结构体的方法如何定义泛型,包括方法和关联函数。值的注意的是在调用关联函数的时候,我们指定了具体的类型,可以试一下如果不指定会怎么样?不指定编译会报错,为什么会报错呢?这里涉及Rust中泛型的实现方式。

use std::fmt::Display;fn main() {let r1 = RecTangle {width: 1,height: 2.0,};println!("r1.width={}", r1.width);println!("r1.height={}", r1.height);r1.custom_print1("hello");RecTangle::<i32, i32>::custom_print2("hello");
}struct RecTangle<T, U> {width: T,height: U,
}impl<T, U> RecTangle<T, U> {fn get_width(&self) -> &T {&self.width}fn get_height(&self) -> &U {&self.height}fn custom_print1<E: Display>(&self, other: E) {println!("other={}", other);}fn custom_print2<E: Display>(text: E) {println!("text = {}", text);}
}

单态化

我们来聊一聊Rust中泛型的实现方式:单态化。

有了解过Java中的泛型实现可以知道,Java中的泛型是伪泛型,存在泛型擦除,不管什么泛型最终都是Object。但是Rust中的泛型实现不一样,他是依赖编译期将存在的类型都固定死。什么意思呢?假设我们定义了一个泛型函数,在整个程序中,该泛型可以是i32,f64.那么在编译期间,Rust编译器会根据类型推断生成具体类型的代码,比如这个函数会生成i32和f64对应的函数,在调用函数的地方也会替换为i32和f64的函数,这种行为就叫做单态化,单态化的好处是类型确定且性能优化。我们提供一段代码解释一下,下面的代码中我们定义了泛型函数,在main方法中,调用了&str和i32和f64类型的函数,那么在编译期间,会生成对应类型的my_print函数,并且在main方法中替换函数。

use std::fmt::Display;fn main() {my_print("hello");my_print(12);my_print(3.14);}fn my_print<T: Display>(input: T) {println!("input is {}", input);
}

特质

Rust中的特质trait可以类比于Java中的接口interface理解。

在下面的代码中,我们定义了一个Trait:Say;并且定义了抽象方法say_hi,又定义了三个不同的结构体,并且为这些结构体实现了Say特质。

use std::fmt::Display;fn main() {let p1 = Person {name: String::from("p"),};let d1 = Dog {name: String::from("d1"),};let c1 = Cat {name: String::from("c1"),};p1.say_hi();d1.say_hi();c1.say_hi();}trait Say {fn name_f() {println!("我是接口Say")}fn say_hi(&self);
}struct Person {name: String,
}impl Say for Person {fn say_hi(&self) {println!("{} say hi", self.name);}
}struct Dog {name: String,
}impl Say for Dog {fn say_hi(&self) {println!("{} say wang", self.name);}
}struct Cat {name: String,
}impl Say for Cat {fn say_hi(&self) {println!("{} say miao", self.name);}
}

多态

由于Rust中没有继承的概念,不像Java等语言,有父类和子类的概念,在java代码中经常看到使用父类接收子类对象的代码,在Rust中都是依赖Trait来实现这样的功能的。Trait可以当作参数也可以当作返回值。

use std::fmt::Display;fn main() {let p1 = Person {name: String::from("p"),};let d1 = Dog {name: String::from("d1"),};let c1 = Cat {name: String::from("c1"),};custom_print(&p1);p1.say_hi();}fn custom_print(input: &impl Say) {input.say_hi();
}trait Say {fn name_f() {println!("我是接口Say")}fn say_hi(&self);
}struct Person {name: String,
}impl Say for Person {fn say_hi(&self) {println!("{} say hi", self.name);}
}struct Dog {name: String,
}impl Say for Dog {fn say_hi(&self) {println!("{} say wang", self.name);}
}struct Cat {name: String,
}impl Say for Cat {fn say_hi(&self) {println!("{} say miao", self.name);}
}

上面的代码是用Trait当作参数的例子,定义了custom_print函数,函数的参数使用impl Say表示接收一个实现了Say特质的参数,注意这里我使用了&表示这只是一个引用,我们复习一下所有权转移的知识,如果不加&,所以权会转移给入参,当函数运行结束后,这一块内存会被释放,于是我们还在想函数调用结束后,使用结构体对象的话,就会报错,于是我们使用&借用只读权限,这样不会报错,由此可见,所有权无处不在。

下面的代码展示了如何使用Trait作为函数的返回值

在函数的返回值处使用impl Say表示函数返回的是一个实现了Say特质的对象。

use std::fmt::Display;fn main() {let p1 = Person {name: String::from("p"),};let d1 = Dog {name: String::from("d1"),};let c1 = Cat {name: String::from("c1"),};Say::name_f();Person::name_f();custom_print(&p1);p1.say_hi();let xx = new_say();xx.say_hi();}fn new_say() -> impl Say {Dog {name: String::from("www"),}
}fn custom_print(input: &impl Say) {input.say_hi();
}trait Say {fn name_f() {println!("我是接口Say")}fn say_hi(&self);
}struct Person {name: String,
}impl Say for Person {fn say_hi(&self) {println!("{} say hi", self.name);}
}struct Dog {name: String,
}impl Say for Dog {fn say_hi(&self) {println!("{} say wang", self.name);}
}struct Cat {name: String,
}impl Say for Cat {fn say_hi(&self) {println!("{} say miao", self.name);}
}

总结

这节课讲述了Rust中面向对象的部分,对于泛型,Rust中泛型实现方式和其他语言不用,采用了单态化的方式在编译器生成代码,Rust将很多工作都移动到了编译器执行,这也是编译时间长的原因之一,Rust中的特质和Scala中的特质Java中的接口类似。

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

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

相关文章

C++进阶:哈希表实现

目录 一:哈希表的概念 1.1直接定址法 1.2哈希冲突 1.3负载因子 1.4实现哈希函数的方法 1.4.1除法散列法/除留余数法 1.4.2乘法散列法 1.4.3全域散列法 1.5处理哈希冲突 1.5.1开放地址法 线性探测 二次探测 ​编辑 双重散列 1.5.2链地址法 二.代码实现 2.1开放地址…

鸿蒙NEXT开发案例:血型遗传计算

【引言】 血型遗传计算器是一个帮助用户根据父母的血型预测子女可能的血型的应用。通过选择父母的血型&#xff0c;应用程序能够快速计算出孩子可能拥有的血型以及不可能拥有的血型。这个过程不仅涉及到了简单的数据处理逻辑&#xff0c;还涉及到UI设计与交互体验的设计。 【…

(十八)JavaWeb后端开发案例——会话/yml/过滤器/拦截器

目录 1.业务逻辑实现 1.1 登录校验技术——会话 1.1.1Cookie 1.1.2session 1.1.3JWT令牌技术 2.参数配置化 3.yml格式配置文件 4.过滤器Filter 5.拦截器Interceptor 1.业务逻辑实现 Day10-02. 案例-部门管理-查询_哔哩哔哩_bilibili //Controller层/*** 新增部门*/Pos…

2024.5 AAAiGLaM:通过邻域分区和生成子图编码对领域知识图谱对齐的大型语言模型进行微调

GLaM: Fine-Tuning Large Language Models for Domain Knowledge Graph Alignment via Neighborhood Partitioning and Generative Subgraph Encoding 问题 如何将特定领域知识图谱直接整合进大语言模型&#xff08;LLM&#xff09;的表示中&#xff0c;以提高其在图数据上自…

amd显卡和nVidia显卡哪个好 amd和英伟达的区别介绍

AMD和英伟达是目前市场上最主要的两大显卡品牌&#xff0c;它们各有自己的特点和优势&#xff0c;也有不同的适用场景和用户群体。那么&#xff0c;AMD显卡和英伟达显卡到底哪个好&#xff1f;它们之间有什么区别&#xff1f;我们又该如何选择呢&#xff1f;本文将从以下几个方…

接口加密了怎么测?

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1、定义加密需求 确定哪些数据需要进行加密。这可以是用户敏感信息、密码、身份验证令牌等。确定使用的加密算法&#xff0c;如对称加密&#xff08;如AES&am…

接口上传视频和oss直传视频到阿里云组件

接口视频上传 <template><div class"component-upload-video"><el-uploadclass"avatar-uploader":action"uploadImgUrl":on-progress"uploadVideoProcess":on-success"handleUploadSuccess":limit"lim…

springboot基于数据挖掘的广州招聘可视化分析系统

摘 要 基于数据挖掘的广州招聘可视化分析系统是一个创新的在线平台&#xff0c;旨在通过深入分析大数据来优化和改善广州地区的招聘流程。系统利用Java语言、MySQL数据库&#xff0c;结合目前流行的 B/S架构&#xff0c;将广州招聘可视化分析管理的各个方面都集中到数据库中&a…

VIM的下载使用与基本指令【入门级别操作】

VIM——超级文本编辑器 在当今时代&#xff0c;功能极其复杂的代码编辑器和集成开发环境&#xff08;IDE&#xff09;有很多。 但如果只想要一个超轻量级的代码编辑器&#xff0c;用于 Unix、C 或其他语言/系统&#xff0c;而不需要那些华而不实的功能&#xff0c;该怎么办呢&…

心情追忆-首页“毒“鸡汤AI自动化

之前&#xff0c;我独自一人开发了一个名为“心情追忆”的小程序&#xff0c;旨在帮助用户记录日常的心情变化及重要时刻。我从项目的构思、设计、前端&#xff08;小程序&#xff09;开发、后端搭建到最终部署。经过一个月的努力&#xff0c;通过群聊分享等方式&#xff0c;用…

开源代码统计工具cloc的简单使用

一.背景 公司之前开发了个小系统&#xff0c;要去申请著作权&#xff0c;需要填写代码数量。应该怎么统计呢&#xff1f;搜索了一下&#xff0c;还是用开源工具cloc吧&#xff01;我的操作系统是windows&#xff0c;代码主要是java项目和vue项目。 二.到哪里找 可以去官方下载…

基于单片机的条形码识别结算设计

本设计基于单片机的条形码辨识与结算系统。该系统主要用于超市、商场等场所的商品结算&#xff0c;实现了在超市内对不同种类商品进行自动识别及自动分类结算的功能。该系统由STM32F103C8T6单片机、摄像头、显示、蜂鸣器报警、按键和电源等多个模块构成。该系统可实现商品自动识…

进程间通信的信号艺术:机制、技术与实战应用深度剖析

目录 1 什么是信号 2 为什么要有信号 3 对于信号的反应 3.1 默认行为 3.2 signal()函数 -- 自定义行为对信号做出反应 3.3 对信号进行忽略 4 信号的产生的类型 4.1 kill命令 4.2 键盘输入产生信号 4.3 系统调用接口 4.3.1 kill() 4.3.2 raise() 函数 4.4 软件条件 …

美畅物联丨JT/T 808 终端设备如何加入畅联云平台

在道路运输行业中&#xff0c;JT/T 808终端设备的应用正变得越来越广泛&#xff0c;把该设备接入畅联云平台&#xff0c;能够达成更高效的车辆管理与监控功能。今天&#xff0c;我们就来探讨一下JT/T 808终端设备接入畅联云平台的步骤与要点。 一、了解畅联云平台接入要求 首先…

【微服务】SpringBoot 整合ELK使用详解

目录 一、前言 二、为什么需要ELK 三、ELK介绍 3.1 什么是elk 3.2 elk工作原理 四、ELK搭建 4.1 搭建es环境 4.1.1 获取es镜像 4.1.2 启动es容器 4.1.3 配置es参数 4.1.4 重启es容器并访问 4.2 搭建kibana 4.2.1 拉取kibana镜像 4.2.2 启动kibana容器 4.2.3 修改…

jenkins的安装(War包安装)

‌Jenkins是一个开源的持续集成工具&#xff0c;基于Java开发&#xff0c;主要用于监控持续的软件版本发布和测试项目。‌ 它提供了一个开放易用的平台&#xff0c;使软件项目能够实现持续集成。Jenkins的功能包括持续的软件版本发布和测试项目&#xff0c;以及监控外部调用执行…

低速接口项目之串口Uart开发(一)——串口UART

本节目录 一、串口UART 二、串口协议 三、串口硬件 四、往期文章链接本节内容 一、串口UART 串口UART,通用异步收发传输器&#xff08;Universal Asynchronnous Receiver / Transmitter&#xff09;,一种异步收发传输器&#xff0c;全双工传输。数据发送时&#xff0c;将并行…

WEB攻防-通用漏洞SQL注入Tamper脚本Base64Jsonmd5等

知识点&#xff1a; 1、数据表现格式类型注入&#xff1b; 2、字符转义绕过-宽字节注入&#xff1b; 3、数字&字符&搜索&编码&加密等&#xff1b; 参考资料&#xff1a; https://www.cnblogs.com/bmjoker/p/9326258.html SQL注入课程体系&#xff1a; 1、…

[Unity]TileMap开发,TileMap地图缝隙问题

环境&#xff1a; windows11 unity 2021.3.14f1c1 tilemap使用的图是美术已经拼接到一起的整图&#xff0c;块与块之间没有留缝隙 问题&#xff1a; TileMap地图直接在Unity中使用时&#xff0c;格子边缘会出现缝隙&#xff0c;移动或缩放地图时较明显。 解决方案&#x…

第75期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…