Rust语言从入门到入坑——(8)Rust泛型与特性

news2024/11/25 13:14:03

文章目录

  • 0、引入
  • 1、泛型
    • 1.1、在函数中使用
    • 1.2、在结构体中使用
  • 2、特性
    • 2.1 默认特性
    • 2.2 特性做参数
    • 2.3 特性做返回值
    • 2.4 有条件实现方法
  • 3、总结


在这里插入图片描述

0、引入

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


  • C++ 语言中用"模板"来实现泛型,而 C 语言中没有泛型的机制,这也导致 C 语言难以构建类型复杂的工程。
  • 泛型机制是编程语言用于表达类型抽象的机制,一般用于功能确定、数据类型待定的类,如链表、映射表等。

1、泛型

1.1、在函数中使用

//数组排序,只针对i32数据
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));
}

//要支持其他数据类型就可以使用函数泛型
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]
}

1.2、在结构体中使用

在之前我们学习的 Option 和 Result 枚举类就是泛型的。
Rust 中的结构体和枚举类都可以实现泛型机制。

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

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


也可以多个T
struct Point<T1, T2> {
    x: T1,
    y: T2
}
/结构体方法也可以使用
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());
}

注:
1、结构体与枚举类都可以定义方法,那么方法也应该实现泛型的机制,否则泛型的类将无法被有效的方法操作。
2、使用时并没有声明类型,这里使用的是自动类型机制,但不允许出现类型不匹配的情况如下。


2、特性

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

trait Descriptive {
    fn describe(&self) -> String;
}
//我们用它实现一个结构体
struct Person {
    name: String,
    age: u8
}

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

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

2.1 默认特性

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

trait Descriptive {
    fn describe(&self) -> String {
        String::from("[Object]")
    }
}

struct Person {
    name: String,
    age: u8
}

impl Descriptive for Person {                   //2重新定义
    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
如果去掉 //2重新定义 
则结果:[Object]

该处使用的url网络请求的数据。

2.2 特性做参数

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

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

//一种等效的语法糖格式
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)


//复杂的实现关系可以使用 where 关键字简化
fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U)
//简化为
fn some_function<T, U>(t: T, u: U) -> i32
    where T: Display + Clone,
          U: Clone + Debug

所以rust学到后面之后越来越难看得懂了,因为很多都是简化,这是他写法上的便利,但是阅读上的困惑,只有真正习惯的人才会得心应手

2.3 特性做返回值

格式如下:

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

注意:但是有一点,特性做返回值只接受实现了该特性的对象做返回值且在同一个函数中所有可能的返回值类型必须完全一样。

2.4 有条件实现方法

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

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

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

3、总结

Rust泛型和特性虽然很强大,但是就和C++中泛型一样,大多数的情况下用不到,可以作为高级特性掌握。


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

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

相关文章

MySQL数据库——备份与恢复

MySQL数据库——备份与恢复 一、数据备份的重要性二 、数据库备份的分类和备份策略1 数据库备份的分类2 数据库的备份策略3常见的备份方法 三、MySQL完全备份1 什么是完全备份2 完全备份的优缺点3 完全备份的方法 四、完全备份的操作1.物理冷备份与恢复2.mysqldump 备份与恢复3…

Arduino IDE 2.1.0 下安装ESP32

很久没玩Arduino了。新装了一台电脑&#xff0c;想重新捡起ESP32-WROOM-32开发板玩玩。官网上下了Arduino IDE 2.1.0&#xff0c;然后配置ESP32开发环境。 下载及安装Arduino IDE略过不谈。装完后还是老规矩&#xff0c;文件 -> 首选项&#xff0c;先把自定义板管理地址填入…

如何与西门子200 PLC建立连接

更多关于西门子S7-200PLC内容请查看&#xff1a;西门子200系列PLC学习课程大纲 与西门子200 PLC建立连接很简单&#xff0c;分为以下几步&#xff1a; 1.给S7-200PLC通电 按下图1-1所示连接PLC端口&#xff0c;然后给PLC供电&#xff0c;有直流24V和交流220V两种供电方式&am…

力扣----环形链表

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 金句分享:…

9i物联网浏览器(cef_114.2.110114.2.100支持H264视频)WinForm-CefSharp114(5735)视频版本体验

更新:2023.6.25 版本:Cef_114.2.110和114.2.100+chromium-114.0.5735.134的32位和64位 说明:支持图片,mp3,mp4(H264)多媒体 测试环境:windows server 2019 测试网址:www.html5test.com 1.包下载地址 1.1 https://www.nuget.org/packages/CefSharp.Common/ 1.2 https…

SpringCloud Alibaba学习笔记

SpringCloud Alibaba学习笔记 1、项目搭建 版本依赖关系&#xff1a;https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E 1.1、搭建父工程 spring-initializr构建项目 清理父工程中不必要的文件/文件夹 修改pom.xml文件 <…

Linux进程信号【信号产生】

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; Linux学习之旅 &#x1f383;操作环境&#xff1a; CentOS 7.6 阿里云远程服务器 文章目录 &#x1f307;前言&#x1f3d9;️正文1、进程信号基本概念1.1、什么是信号&#xff1f;1.2、信号的作用1.3、信号的…

十四、go语言的指针

一、指针 1.1 指针的概念 指针是存储另一个变量的内存地址的变量。 我们都知道&#xff0c;变量是一种使用方便的占位符&#xff0c;用于引用计算机内存地址。 一个指针变量可以指向任何一个值的内存地址它指向那个值的内存地址。 在上面的图中&#xff0c;变量b的值为156&…

怎么输入文字生成绘画图?分享一份文字生成绘画教程

你是否曾经幻想过&#xff0c;将文字化为绘画&#xff0c;将思维转化为图像&#xff0c;让你的想象力得到更好的释放与表达&#xff1f;现在&#xff0c;这一切都变得可能了&#xff01;通过一些绘画软件&#xff0c;你可以轻松生成各种风格的绘画图。只需要一个创意的想法和一…

Selenium教程__POM架构(17)

POM是Page Object Model的简称&#xff0c;它是一种设计思想&#xff0c;意思是&#xff0c;把每一个页面&#xff0c;当做一个对象&#xff0c;页面的元素和元素之间操作方法就是页面对象的属性和行为。 POM一般使用三层架构&#xff0c;分别为&#xff1a;基础封装层、页面对…

SkyWalking--traceId的作用

原文网址&#xff1a;SkyWalking--traceId的作用_IT利刃出鞘的博客-CSDN博客 简介 本文介绍SkyWalking中traceId的作用。 traceId是什么 SkyWalking的一个核心功能就是&#xff1a;链路追踪。链路追踪就是跟踪一个请求的所有链路&#xff0c;而这个链路都是通过一个id来串起…

数据结构--算法空间复杂度

数据结构–算法空间复杂度 只需关注存储空间大小与问题规模相关的变量 计算规则与算法的时间复杂度类似 eg&#xff1a; S ( n ) O ( n 2 ) O ( n ) O ( 1 ) O ( n 2 ) S(n) O(n^2)O(n)O(1) O(n^2) S(n)O(n2)O(n)O(1)O(n2) 知识点回顾 & 重要考点

java list集合数据去重方式

1.概述 最近又是一轮代码review , 发现了一些实现去重的代码&#xff0c;在使用 list.contain … 我沉思&#xff0c;是不是其实很多初学者也存在这种去重使用问题&#xff1f; 所以我选择把这个事情整出来&#xff0c;分享一下。 2.contain 去重 首先是造出一个 List 模拟…

[CKA]考试之七层负载均衡Ingress

由于最新的CKA考试改版&#xff0c;不允许存储书签&#xff0c;本博客致力怎么一步步从官网把答案找到&#xff0c;如何修改把题做对&#xff0c;下面开始我们的 CKA之旅 题目为&#xff1a; Task 如下创建一个新的nginx Ingress资源&#xff1a; 名称: pong Namespace: i…

掌握这些容易被忽略的Vue细节,轻松排查问题,省时省力!

v-bind 绑定的值是 null 或者 undefined v-bind 如果绑定的值是 null 或者 undefined&#xff0c;那么该 attribute 将会从渲染的元素上移除。 当attribute 为布尔型时&#xff0c; 行为略有不同。 <button :disabled"isButtonDisabled">Button</button&…

CSS之平面转换

简介 作用&#xff1a;为元素添加动态效果&#xff0c;一般与过渡配合使用 概念&#xff1a;改变盒子在平面内的形态&#xff08;位移、旋转、缩放、倾斜&#xff09; 平面转换也叫 2D 转换&#xff0c;属性是 transform 平移 transform: translate(X轴移动距离, Y轴移动距…

@Valid接口参数校验怎么做,详细教程

接口参数校验教程 一、在字段上可以使用这个注解来设置校验 Null&#xff1a;被注释的元素必须为null NotNull&#xff1a;被注释的元素不能为null AssertTrue&#xff1a;该字段只能为true AssertFalse&#xff1a;该字段的值只能为false Min("value","messa…

机器学习:监督学习

目前&#xff0c;在机器学习系统中&#xff0c;监督学习&#xff08;Supervised Learning&#xff09;占主导地位。由于监督学习的任务定义明确&#xff0c;例如识别垃圾邮件或预测降水&#xff0c;因此它比无监督学习具有更多潜在用例&#xff1b;而与强化学习相比&#xff0c…

剑指offer(C++)-JZ51:数组中的逆序对(算法-排序)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 题目描述&#xff1a; 在数组中的两个数字&#xff0c;如果前面一个数字大于后面的数字&#xff0c;则这两个数字组成一个逆序对…

Java IO 学习总结(四)BufferedReader 缓冲字符流

Java IO 学习总结&#xff08;一&#xff09;输入流/输出流 Java IO 学习总结&#xff08;二&#xff09;File 类 Java IO 学习总结&#xff08;三&#xff09;BufferedInputStream Java IO 学习总结&#xff08;四&#xff09;BufferedReader 缓冲字符流 前言&#xff1a; 学…