Rust泛型与trait特性,模仿接口的实现

泛型是一个编程语言不可或缺的机制。

C++ 语言中用"模板"来实现泛型,而 C 语言中没有泛型的机制,这也导致 C 语言难以构建类型复杂的工程。

泛型机制是编程语言用于表达类型抽象的机制,一般用于功能确定、数据类型待定的类,如链表、映射表等。

在函数中定义泛型

这是一个对整型数字选择排序的方法:

实例

fn max(array: &[i32]) -> i32 {let mut max_index = 0;let mut i = 1;while i < array.len() {if array[i] > array[max_index] {max_index = i;}i += 1;}array[max_index]
}fn main() {let a = [2, 4, 6, 3, 1];println!("max = {}", max(&a));
}

运行结果:

max = 6

这是一个简单的取最大值程序,可以用于处理 i32 数字类型的数据,但无法用于 f64 类型的数据。通过使用泛型我们可以使这个函数可以利用到各个类型中去。但实际上并不是所有的数据类型都可以比大小,所以接下来一段代码并不是用来运行的,而是用来描述一下函数泛型的语法格式:

实例

fn max<T>(array: &[T]) -> T {let mut max_index = 0;let mut i = 1;while i < array.len() {if array[i] > array[max_index] {max_index = i;}i += 1;}array[max_index]
}

结构体与枚举类中的泛型

在之前我们学习的 Option 和 Result 枚举类就是泛型的。

Rust 中的结构体和枚举类都可以实现泛型机制。

struct Point<T> {x: T,y: T
}

这是一个点坐标结构体,T 表示描述点坐标的数字类型。我们可以这样使用:

let p1 = Point {x: 1, y: 2};
let p2 = Point {x: 1.0, y: 2.0};

使用时并没有声明类型,这里使用的是自动类型机制,但不允许出现类型不匹配的情况如下:

let p = Point {x: 1, y: 2.0};

x 与 1 绑定时就已经将 T 设定为 i32,所以不允许再出现 f64 的类型。如果我们想让 x 与 y 用不同的数据类型表示,可以使用两个泛型标识符:

struct Point<T1, T2> {x: T1,y: T2
}

在枚举类中表示泛型的方法诸如 Option 和 Result:

enum Option<T> {Some(T),None,
}enum Result<T, E> {Ok(T),Err(E),
}

结构体与枚举类都可以定义方法,那么方法也应该实现泛型的机制,否则泛型的类将无法被有效的方法操作。

实例

struct Point<T> {x: T,y: T,
}impl<T> Point<T> {fn x(&self) -> &T {&self.x}
}fn main() {let p = Point { x: 1, y: 2 };println!("p.x = {}", p.x());
}

运行结果:

p.x = 1

注意,impl 关键字的后方必须有 <T>,因为它后面的 T 是以之为榜样的。但我们也可以为其中的一种泛型添加方法:

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

impl 块本身的泛型并没有阻碍其内部方法具有泛型的能力:

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,}}
}

方法 mixup 将一个 Point<T, U> 点的 x 与 Point<V, W> 点的 y 融合成一个类型为 Point<T, W> 的新点。


特性

特性(trait)概念接近于 Java 中的接口(Interface),但两者不完全相同。特性与接口相同的地方在于它们都是一种行为规范,可以用于标识哪些类有哪些方法。

特性在 Rust 中用 trait 表示:

trait Descriptive {fn describe(&self) -> String;
}

Descriptive 规定了实现者必需有 describe(&self) -> String 方法。

我们用它实现一个结构体:

实例

struct Person {name: String,age: u8
}impl Descriptive for Person {fn describe(&self) -> String {format!("{} {}", self.name, self.age)}
}

格式是:

impl <特性名> for <所实现的类型名>

Rust 同一个类可以实现多个特性,每个 impl 块只能实现一个。

默认特性

这是特性与接口的不同点:接口只能规范方法而不能定义方法,但特性可以定义方法作为默认方法,因为是"默认",所以对象既可以重新定义方法,也可以不重新定义方法使用默认的方法:

实例

trait Descriptive {fn describe(&self) -> String {String::from("[Object]")}
}struct Person {name: String,age: u8
}impl Descriptive for Person {fn describe(&self) -> String {format!("{} {}", self.name, self.age)}
}fn main() {let cali = Person {name: String::from("Cali"),age: 24};println!("{}", cali.describe());
}

运行结果:

Cali 24

如果我们将 impl Descriptive for Person 块中的内容去掉,那么运行结果就是:

[Object]

特性做参数

很多情况下我们需要传递一个函数做参数,例如回调函数、设置按钮事件等。在 Java 中函数必须以接口实现的类实例来传递,在 Rust 中可以通过传递特性参数来实现:

fn output(object: impl Descriptive) {println!("{}", object.describe());
}

任何实现了 Descriptive 特性的对象都可以作为这个函数的参数,这个函数没必要了解传入对象有没有其他属性或方法,只需要了解它一定有 Descriptive 特性规范的方法就可以了。当然,此函数内也无法使用其他的属性与方法。

特性参数还可以用这种等效语法实现:

fn output<T: Descriptive>(object: T) {println!("{}", object.describe());
}

这是一种风格类似泛型的语法糖,这种语法糖在有多个参数类型均是特性的情况下十分实用:

fn output_two<T: Descriptive>(arg1: T, arg2: T) {println!("{}", arg1.describe());println!("{}", arg2.describe());
}

特性作类型表示时如果涉及多个特性,可以用 + 符号表示,例如:

fn notify(item: impl Summary + Display)
fn notify<T: Summary + Display>(item: T)

注意:仅用于表示类型的时候,并不意味着可以在 impl 块中使用。

复杂的实现关系可以使用 where 关键字简化,例如:

fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U)

可以简化成:

fn some_function<T, U>(t: T, u: U) -> i32where T: Display + Clone,U: Clone + Debug

在了解这个语法之后,泛型章节中的"取最大值"案例就可以真正实现了:

实例

trait Comparable {fn compare(&self, object: &Self) -> i8;
}fn max<T: Comparable>(array: &[T]) -> &T {let mut max_index = 0;let mut i = 1;while i < array.len() {if array[i].compare(&array[max_index]) > 0 {max_index = i;}i += 1;}&array[max_index]
}impl Comparable for f64 {fn compare(&self, object: &f64) -> i8 {if &self > &object { 1 }else if &self == &object { 0 }else { -1 }}
}fn main() {let arr = [1.0, 3.0, 5.0, 4.0, 2.0];println!("maximum of arr is {}", max(&arr));
}

运行结果:

maximum of arr is 5

Tip: 由于需要声明 compare 函数的第二参数必须与实现该特性的类型相同,所以 Self (注意大小写)关键字就代表了当前类型(不是实例)本身。

特性做返回值

特性做返回值格式如下:

实例

fn person() -> impl Descriptive {Person {name: String::from("Cali"),age: 24}
}

但是有一点,特性做返回值只接受实现了该特性的对象做返回值且在同一个函数中所有可能的返回值类型必须完全一样。比如结构体 A 与结构体 B 都实现了特性 Trait,下面这个函数就是错误的:

实例

fn some_function(bool bl) -> impl Descriptive {if bl {return A {};} else {return B {};}
}

有条件实现方法

impl 功能十分强大,我们可以用它实现类的方法。但对于泛型类来说,有时我们需要区分一下它所属的泛型已经实现的方法来决定它接下来该实现的方法:

struct A<T> {}impl<T: B + C> A<T> {fn d(&self) {}
}

这段代码声明了 A<T> 类型必须在 T 已经实现 B 和 C 特性的前提下才能有效实现此 impl 块。

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

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

相关文章

三维GIS的业务导向

的确&#xff0c;目前三维GIS以做特效居多&#xff0c;酷炫、亮眼&#xff0c;从二维转到三维&#xff0c;第一眼就给人眼前一亮的感觉&#xff0c;就凭这一项&#xff0c;很多客户就会买单&#xff0c;GIS的客户以政府、科研院所、特种行业为主&#xff0c;买过一次单后&#…

【QT】定时器事件应用

public&#xff1a;//定时器void timerEvent(QTimerEvent *);private:int id;#include <QTimerEvent> //QWidget 默认不追踪鼠标事件 mylabel::mylabel(QWidget *parent) : QLabel(parent) {this->setMouseTracking(true);//启动定时器//参数1&#xff1a;触发定时器的…

JavaScript基础Ⅱ

目录 第2章 JavaScript基础语法(掌握) 11-JS代码调试 12-JS函数 第3章 JS事件 14-事件的绑定方式 常用事件(了解) 15-常用事件 第4章 JS内置对象(掌握) 16-数组 17-日期 18-数学运算 19-数字 20-全局函数 第2章 JavaScript基础语法(掌握) 11-JS代码调试 12-JS函数…

MSCKF3讲:后端理论推导(上)

MSCKF3讲&#xff1a;后端理论推导&#xff08;上&#xff09; 文章目录 MSCKF3讲&#xff1a;后端理论推导&#xff08;上&#xff09;1 MSCKF中的状态变量① IMU状态:② cam0状态&#xff1a;③ IMU和cam0间状态关系 2 微分方程递推&#xff08;数值解&#xff09;3 IMU状态预…

视频压缩软件哪个好用?强推这五款压缩软件

在数字化时代&#xff0c;我们每天都会接触到大量的视频内容&#xff0c;无论是在工作中制作视频&#xff0c;还是在日常生活中分享或观看。然而&#xff0c;随着高清晰度和4K视频的普及&#xff0c;视频文件的大小也逐渐增加&#xff0c;对存储空间和网络传输速度提出了更高的…

python基础(11)《Allure报告中的组件用法》

使用 官方教程&#xff1a;https://docs.qameta.io/allure 入门 想要看到allure报告&#xff0c;需要做2个步骤&#xff1a; 1、pytest执行时关联allure&#xff1a;pytest命令带上--alluredir 结果存放目录或--alluredir结果存放目录&#xff1b; 2、打开执行报告&#xff…

项目管理工具进度猫:自我管理的应用

在飞速发展的现代社会中&#xff0c;每个人都面临着巨大的竞争压力&#xff0c;如何在这激烈的环境中脱颖而出&#xff0c;实现个人的成长与成功&#xff1f;答案就在我们的日常行为中——自我管理。 一、自我管理的定义 自我管理&#xff0c;简单来说&#xff0c;就是对自己…

Linux-网络-011

1网络协议模型 1.1【OSI】协议模型 1.1.1应用层 实际发送的数据应用层:HTTP 超文本传输协议HTTPS FTP 文件传输协议TFTP 简单文本传输协议SMTP 邮件传输协议MQTT TELNET ..1.1.2表示层 发送的数据是否加密1.1.3会话层 是否建立会话连接1.1.4传输层 数据…

犯难了,99元一年服务器选腾讯云还是阿里云?

腾讯云服务器99元一年是真的吗&#xff1f;真的&#xff0c;只是又降价了&#xff0c;现在只要61元一年&#xff0c;配置为2核2G3M轻量应用服务器&#xff0c;40GB SSD盘&#xff0c;腾讯云百科txybk.com分享腾讯云官方活动购买链接 https://curl.qcloud.com/oRMoSucP 活动打开…

Java精品项目--第6期基于SpringBoot的茶叶商城的设计分析与实现

项目技术栈 SpringBootMavenMySQLJAVAMybatis-PLusVue.js&#xff08;非前后端分离&#xff09;Element-UI&#xff08;非前后端分离&#xff09;… 表截图 项目截图

UE4 Niagara 关卡1.4官方案例解析

sprites can face the camera&#xff0c;or they can face any arbitrary vector&#xff0c;in this case the vector between the center of the system and the particle itself&#xff08;粒子可以面对摄影机&#xff0c;也可以面对任意向量&#xff0c;在这个实例中的向…

纯手工搭建一个springboot maven项目

前言&#xff1a;idea社区版无法自动搭建项目&#xff0c;手动搭建的经验分享如下&#xff1a; 1 包结构 参考下图&#xff1a; 2 项目结构 3 maven依赖 具体的项目包结构如下图&#xff1a; 依据这个项目包结构配置一个springboot 的 pom依赖&#xff1a; <?xml ve…

java多线程实现同步的方式介绍

在 Java 多线程编程中&#xff0c;同步是确保多个线程在访问共享资源时的正确性和一致性的重要机制。Java 提供了多种方式来实现线程同步&#xff0c;每种方式都有其特点和适用场景。以下是一些常见的同步方式及其特点和应用场景&#xff0c;以及简单的例子来说明它们的用法。 …

低成本便捷使用最新Claude 3模型的方法!

3 月 4 日&#xff0c;被称为 OpenAI 最强竞争对手的大模型公司 Anthropic 宣布推出 Claude3 系列模型&#xff0c;与 Gemini 类似&#xff0c;模型按照大小分为三个&#xff1a;Claude 3 Haiku、Claude 3 Sonnet 和 Claude 3 Opus。Opus 目前在官方发布的测试成绩中全方位超越…

电脑主机弄丢后赔偿却还收150元费用?

“电脑主机丢失后&#xff0c;我被收取了150元的赔偿费。” 22日&#xff0c;家住临沂市沂水县龙家泉镇的吴女士拨打热线电话反映&#xff0c;家里电脑主机出现问题&#xff0c;她把电脑放在镇上的一家店里。 我去电脑维修店修好了&#xff0c;对方丢了&#xff0c;但他们还是收…

vscode中eslint插件不生效问题

case: 最近使用webpack打包js资源中使用到了VS Code中的eslint插件辅助eslint plugin对代码进行校验&#xff0c;在.eslintrc.js文件中以及webpack.config.js配置好后&#xff0c; 在控制台运行npx webpack可以读取到eslint plugin的检测结果 一、eslint插件读取项目中.eslint…

Nodejs 第五十三章(serverLess)

什么是serverLess? serverLess并不是一个技术&#xff0c;他只是一种架构模型&#xff0c;(无服务器架构)&#xff0c;在传统模式下&#xff0c;我们部署一个服务&#xff0c;需要选择服务器Linux,windows等,并且还要安装环境&#xff0c;熟悉操作系统命令&#xff0c;知晓安…

如何提高LED工程预算的准确性?

LED工程预算的准确性对工程的顺利进行至关重要。预算过高会导致资源浪费&#xff0c;甚至滋生腐败问题&#xff1b;而预算过低则会导致施工进度受阻&#xff0c;影响工程质量和人员工资发放。因此&#xff0c;提高LED工程预算的准确性是工程管理中的重要课题。 以下是一些提高L…

部署YOLOv8模型的实用常见场景

可以的话&#xff0c;GitHub上点个小心心&#xff0c;翻不了墙的xdm&#xff0c;csdn也可以点个赞&#xff0c;谢谢啦 车流量检测&#xff08;开源代码github&#xff09;&#xff1a; test3 meiqisheng/YOLOv8-DeepSORT-Object-Tracking (github.com) 车牌检测&#xff0…

Vue3.2 + vue/cli-service 打包 chunk-vendors.js 文件过大导致页面加载缓慢解决方案

chunk-vendors.js 是/node_modules 目录下的所有模块打包成的包&#xff0c; 但是这包太大导致页面加载很慢&#xff08;我的都要3-4秒了&#xff09;&#xff0c; 这个时候就会出现白屏的情况 解决方案 1、compression-webpack-plugin 插件解决方案 1&#xff09;、安装 npm …