【Rust】——提取函数消除重复代码和泛型

🎃个人专栏:

🐬 算法设计与分析:算法设计与分析_IT闫的博客-CSDN博客

🐳Java基础:Java基础_IT闫的博客-CSDN博客

🐋c语言:c语言_IT闫的博客-CSDN博客

🐟MySQL:数据结构_IT闫的博客-CSDN博客

🐠数据结构:​​​​​​数据结构_IT闫的博客-CSDN博客

💎C++:C++_IT闫的博客-CSDN博客

🥽C51单片机:C51单片机(STC89C516)_IT闫的博客-CSDN博客

💻基于HTML5的网页设计及应用:基于HTML5的网页设计及应用_IT闫的博客-CSDN博客​​​​​​

🥏python:python_IT闫的博客-CSDN博客

🐠离散数学:离散数学_IT闫的博客-CSDN博客

​​​​​​🥽Linux:​​​​Linux_Y小夜的博客-CSDN博客

🚝Rust:Rust_Y小夜的博客-CSDN博客

欢迎收看,希望对大家有用!

目录

🎯提取函数消除重复代码

🎯泛型

🎃函数定义中的泛型

🎃Struct定义中的泛型

🎃Enum定义中的泛型

🎃方法定义中的泛型

🎃泛型代码的功能


🎯提取函数消除重复代码

        泛型允许我们使用一个可以代表多种类型的占位符来替换特定类型,以此来减少代码冗余。在深入了解泛型的语法之前,我们首先来看一种没有使用泛型的减少冗余的方法,即提取一个函数。在这个函数中,我们用一个可以代表多种值的占位符来替换具体的值。接着我们使用相同的技术来提取一个泛型函数!!通过学习如何识别并提取可以整合进一个函数的重复代码,你也会开始识别出可以使用泛型的重复代码。

fn main() {
    let number_list = vec![34, 50, 25, 100, 65];

    let mut largest = &number_list[0];

    for number in &number_list {
        if number > largest {
            largest = number;
        }
    }

    println!("The largest number is {}", largest);
}

        这段代码获取一个整型列表,存放在变量 number_list 中。它将列表的第一个数字的引用放入了变量 largest 中。接着遍历了列表中的所有数字,如果当前值大于 largest 中储存的值,将 largest 替换为这个值。如果当前值小于或者等于目前为止的最大值,largest 保持不变。当列表中所有值都被考虑到之后,largest 将会指向最大值,在这里也就是 100。

        为了消除重复,我们要创建一层抽象,定义一个处理任意整型列表作为参数的函数。这个方案使得代码更简洁,并且表现了寻找任意列表中最大值这一概念。

fn largest(list: &[i32]) -> &i32 {
    let mut largest = &list[0];

    for item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}

fn main() {
    let number_list = vec![34, 50, 25, 100, 65];

    let result = largest(&number_list);
    println!("The largest number is {}", result);

    let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8];

    let result = largest(&number_list);
    println!("The largest number is {}", result);
}

  largest 函数有一个参数 list,它代表会传递给函数的任何具体的 i32值的 slice。函数定义中的 list 代表任何 &[i32]。当调用 largest 函数时,其代码实际上运行于我们传递的特定值上。

涉及的机制经历了如下几步:

  1. 找出重复代码。
  2. 将重复代码提取到了一个函数中,并在函数签名中指定了代码中的输入和返回值。
  3. 将重复代码的两个实例,改为调用函数。

        接下来我们会使用相同的步骤通过泛型来减少重复。与函数体可以处理任意的 list 而不是具体的值一样,泛型也允许代码处理任意类型。

🎯泛型

  • 泛型:提高代码的复用能力

        —处理重复代码的能力

  • 泛型是具体类型或其他属性的抽象代替:

        —你编写的代码不是最终的代码,而是一种模板,里面有一些“占位符”。

        当使用泛型定义函数时,本来在函数签名中指定参数和返回值的类型的地方,会改用泛型来表示。采用这种技术,使得代码适应性更强,从而为函数的调用者提供更多的功能,同时也避免了代码的重复。

        如果要在函数体中使用参数,就必须在函数签名中声明它的名字,好让编译器知道这个名字指代的是什么。同理,当在函数签名中使用一个类型参数时,必须在使用它之前就声明它。为了定义泛型版本的 largest 函数,类型参数声明位于函数名称与参数列表中间的尖括号 <> 中,像这样:

fn largest<T>(list: &[T]) -> &T {

        可以这样理解这个定义:函数 largest 有泛型类型 T。它有个参数 list,其类型是元素为 T 的 slice。largest 函数会返回一个与 T 相同类型的引用。

🎃函数定义中的泛型

fn largest_i32(list: &[i32]) -> &i32 {
    let mut largest = &list[0];

    for item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}

fn largest_char(list: &[char]) -> &char {
    let mut largest = &list[0];

    for item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}

fn main() {
    let number_list = vec![34, 50, 25, 100, 65];

    let result = largest_i32(&number_list);
    println!("The largest number is {}", result);

    let char_list = vec!['y', 'm', 'a', 'q'];

    let result = largest_char(&char_list);
    println!("The largest char is {}", result);
}

        为了参数化这个新函数中的这些类型,我们需要为类型参数命名,道理和给函数的形参起名一样。任何标识符都可以作为类型参数的名字。这里选用 T,因为传统上来说,Rust 的类型参数名字都比较短,通常仅为一个字母,同时,Rust 类型名的命名规范是首字母大写驼峰式命名法(UpperCamelCase)。T 作为 “type” 的缩写是大部分 Rust 程序员的首选。

  largest 函数在它的签名中使用了泛型,统一了两个实现。该示例也展示了如何调用 largest 函数,把 i32 值的 slice 或 char 值的 slice 传给它。

        请注意这些代码还不能编译,不过稍后会解决这个问题。

fn largest<T>(list: &[T]) -> &T {
    let mut largest = &list[0];

    for item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}

fn main() {
    let number_list = vec![34, 50, 25, 100, 65];

    let result = largest(&number_list);
    println!("The largest number is {}", result);

    let char_list = vec!['y', 'm', 'a', 'q'];

    let result = largest(&char_list);
    println!("The largest char is {}", result);
}

如果现在就编译这个代码,会出现如下错误:

        帮助说明中提到了 std::cmp::PartialOrd,这是一个 trait。下一部分会讲到 trait。不过简单来说,这个错误表明 largest 的函数体不能适用于 T 的所有可能的类型。因为在函数体需要比较 T 类型的值,不过它只能用于我们知道如何排序的类型。为了开启比较功能,标准库中定义的 std::cmp::PartialOrd trait 可以实现类型的比较功能(查看附录 C 获取该 trait 的更多信息)。依照帮助说明中的建议,我们限制 T 只对实现了 PartialOrd 的类型有效后代码就可以编译了,因为标准库为 i32 和 char 实现了 PartialOrd

🎃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 };
}

        其语法类似于函数定义中使用泛型。首先,必须在结构体名称后面的尖括号中声明泛型参数的名称。接着在结构体定义中可以指定具体数据类型的位置使用泛型类型。

        注意 Point<T> 的定义中只使用了一个泛型类型,这个定义表明结构体 Point<T> 对于一些类型 T 是泛型的,而且字段 x 和 y 都是 相同类型的,无论它具体是何类型。

        如果想要定义一个 x 和 y 可以有不同类型且仍然是泛型的 Point 结构体,我们可以使用多个泛型类型参数。

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

fn main() {
    let both_integer = Point { x: 5, y: 10 };
    let both_float = Point { x: 1.0, y: 4.0 };
    let integer_and_float = Point { x: 5, y: 4.0 };
}

不过太多的话,代码将难以阅读和理解。

🎃Enum定义中的泛型

和结构体类似,枚举也可以在成员中存放泛型数据类型。

enum Option<T> {
    Some(T),
    None,
}

        现在这个定义应该更容易理解了。如你所见 Option<T> 是一个拥有泛型 T 的枚举,它有两个成员:Some,它存放了一个类型 T 的值,和不存在任何值的None。通过 Option<T> 枚举可以表达有一个可能的值的抽象概念,同时因为 Option<T> 是泛型的,无论这个可能的值是什么类型都可以使用这个抽象。

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

  Result 枚举有两个泛型类型,T 和 EResult 有两个成员:Ok,它存放一个类型 T 的值,而 Err 则存放一个类型 E 的值。这个定义使得 Result 枚举能很方便的表达任何可能成功(返回 T 类型的值)也可能失败(返回 E 类型的值)的操作。

🎃方法定义中的泛型

        在为结构体和枚举实现方法时(像第五章那样),一样也可以用泛型。示例 中展示了示例中定义的结构体 Point<T>,和在其上实现的名为 x 的方法。

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

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

fn main() {
    let p = Point { x: 5, y: 10 };

    println!("p.x = {}", p.x());
}

        注意必须在 impl 后面声明 T,这样就可以在 Point<T> 上实现的方法中使用 T 了。通过在 impl 之后声明泛型 T,Rust 就知道 Point 的尖括号中的类型是泛型而不是具体类型。我们可以为泛型参数选择一个与结构体定义中声明的泛型参数所不同的名称,不过依照惯例使用了相同的名称。在声明泛型类型参数的 impl 中编写的方法将会定义在该类型的任何实例上,无论最终替换泛型类型参数的是何具体类型。

impl Point<f32> {
    fn distance_from_origin(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

        这段代码意味着 Point<f32> 类型会有一个方法 distance_from_origin,而其他 T 不是 f32 类型的 Point<T> 实例则没有定义此方法。这个方法计算点实例与坐标 (0.0, 0.0) 之间的距离,并使用了只能用于浮点型的数学运算符。

        结构体定义中的泛型类型参数并不总是与结构体方法签名中使用的泛型是同一类型。

struct Point<X1, Y1> {
    x: X1,
    y: Y1,
}

impl<X1, Y1> Point<X1, Y1> {
    fn mixup<X2, Y2>(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
        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);
}

        在 main 函数中,定义了一个有 i32 类型的 x(其值为 5)和 f64 的 y(其值为 10.4)的 Pointp2 则是一个有着字符串 slice 类型的 x(其值为 "Hello")和 char 类型的 y(其值为c)的 Point。在 p1 上以 p2 作为参数调用 mixup 会返回一个 p3,它会有一个 i32 类型的 x,因为 x 来自 p1,并拥有一个 char 类型的 y,因为 y 来自 p2println! 会打印出 p3.x = 5, p3.y = c

🎃泛型代码的功能

Rust 通过在编译时进行泛型代码的 单态化(monomorphization)来保证效率。单态化是一个通过填充编译时使用的具体类型,将通用代码转换为特定代码的过程。

let integer = Some(5);
let float = Some(5.0);

        当 Rust 编译这些代码的时候,它会进行单态化。编译器会读取传递给 Option<T> 的值并发现有两种 Option<T>:一个对应 i32 另一个对应 f64。为此,它会将泛型定义 Option<T> 展开为两个针对 i32 和 f64 的定义,接着将泛型定义替换为这两个具体的定义。

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

        泛型 Option<T> 被编译器替换为了具体的定义。因为 Rust 会将每种情况下的泛型代码编译为具体类型,使用泛型没有运行时开销。当代码运行时,它的执行效率就跟好像手写每个具体定义的重复代码一样。这个单态化过程正是 Rust 泛型在运行时极其高效的原因。

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

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

相关文章

Java项目:75 springboot房产销售系统

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 使用房产销售系统分为管理员和用户、销售经理三个角色的权限子模块。 管理员所能使用的功能主要有&#xff1a;首页、个人中心、用户管理、销售经理管…

OpenCV4.9在iOS中安装

返回&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;使用CUDA 为Tegra构建OpenCV-CSDN博客 下一篇&#xff1a; 警告&#xff01; 本教程可以包含过时的信息。 所需软件包 CMake 2.8.8 或更高版本Xcode 4.2 或更高版本 从 G…

品牌出海必读:深入解析成功背后的5大底层逻辑

品牌出海是当今全球化时代中企业发展的重要策略之一。无论是传统制造业还是新兴科技公司&#xff0c;都在不同程度上关注着海外市场的拓展。然而&#xff0c;品牌出海并非仅仅是一个简单的营销策略&#xff0c;其背后蕴含着复杂的底层逻辑。本文Nox聚星将和大家探讨品牌出海的底…

电脑桌面便签软件,好用的电脑桌面便签软件

在数字化时代&#xff0c;我们的工作方式正在发生深刻的变革。作为现代办公一族&#xff0c;提升工作效率&#xff0c;管理好的灵感和待办事项变得尤为重要。而在众多的办公辅助工具中&#xff0c;电脑桌面便签软件以其便捷、实用的特点&#xff0c;深受广大办公族的喜爱。今天…

C语言例4-17:从键盘输入一个年份year(4位十进制数),判断其是否是闰年

算法分析&#xff1a; 如果X能被Y整除&#xff0c;则余数为0&#xff0c;即如果X%Y的值等于0&#xff0c;则表示X能被Y整除。首先将是否是闰年的标志leap初始值设置为0&#xff08;非闰年&#xff09;&#xff0c;仅当year为闰年时将leap的位置为1。 初始代码如下&#xff1a…

踏入IOT安全世界:DIR-815路由器多次溢出漏洞分析复现

前言 在进行IOT安全领域的学习和实践中&#xff0c;经典漏洞的复现是必不可少的一环。本文将介绍一个经典漏洞&#xff0c;涉及到Binwalk、firmware-mod-kit、FirmAE等工具的使用&#xff0c;以及对DIR-815路由器中多次溢出漏洞的复现过程。 固件下载地址&#xff1a;https:/…

安捷伦Agilent 34401A数字万用表

181/2461/8938产品概述&#xff1a; Agilent34401A 万用表将准确性、速度、测量简便性和多功能性结合到坚固的 6 1/2 位数字万用表中&#xff0c;无论在工作台上还是在系统中都同样适用。您可以以 5 1/2 位数的价格获得 6 1/2 位数的性能。除了直流和交流电压、直流和交流电流…

golang+vue微服务电商系统

golangvue微服务电商系统 文章目录 golangvue微服务电商系统一、项目前置准备二、项目简介三、代码GItee地址 golang、vue redis、mysql、gin、nacos、es、kibana、jwt 一、项目前置准备 环境的搭建 官方go开发工程师参考地址&#xff1a;https://blog.csdn.net/qq23001186/cat…

【数据结构与算法】直接插入排序和希尔排序

引言 进入了初阶数据结构的一个新的主题——排序。所谓排序&#xff0c;就是一串记录&#xff0c;按照其中的某几个或某些关键字的大小&#xff08;一定的规则&#xff09;&#xff0c;递增或递减排列起来的操作。 排序的稳定性&#xff1a;在一定的规则下&#xff0c;两个值…

Web3 游戏周报(3.17-3.23)

【3.17-3.23】Web3 游戏行业动态&#xff1a; Saga 宣布成立 Web3 游戏发行部门 Saga Origins Web3 游戏平台 Portal 宣布将于周四开放质押功能 STP 推出基于 Base 的 AI 增强游戏 Layer3 Clique Merlin 生态游戏项目 BitRealms 完成 Pre-Seed 轮融资 Telos 在游戏侧链发布…

Open CASCADE学习|显示文本

目录 1、修改代码 Viewer.h&#xff1a; Viewer.cpp&#xff1a; 2、显示文本 OpenCasCade 你好啊 霜吹花落 1、修改代码 在文章《Open CASCADE学习|显示模型》基础上&#xff0c;增加部分代码&#xff0c;实现对文本显示的支持&#xff0c;具体如下&#xff1a; Viewer…

入门编程,一定要从C语言开始吗?

对于编程入门学习者&#xff0c;C语言肯定不是首选。建议先确定自己的发展方向&#xff0c; 如果打算做Web 开发&#xff0c;可以先从学习HTML,CSS,Javascript开始&#xff0c;后台使用Node.JS&#xff0c;也是用Javascript 来编程, 可降低入门门槛。 在开始前我有一些资料…

MySQL索引优化、SQL优化-持续更新

背景 成本最低的优化手段&#xff0c;面试常问的面试题。 下面主要是讲InnoDB存储引擎的索引&#xff0c;InnoDB也是实际项目用的最多的存储引擎。 优势 提高数据查询效率。 缺点 占用空间、降低了增、删、改速度&#xff0c;因为要维护索引 原理 底层是B树数据结构。 …

关于Web APP 促进临床预测模型进入临床实践的讨论

关于Web APP 促进临床预测模型进入临床实践的讨论 关键词&#xff1a; 临床预测模型、Web APP、实践、标准、讨论、分享 近年来&#xff0c;随着机器学习技术的快速发展&#xff0c;临床预测模型在辅助诊断、预后评估、治疗方案选择等方面展现出巨大潜力。然而&#xff0c;将…

工时表软件:项目管理的效率利器

在当今的项目管理实践中&#xff0c;时间是最宝贵的资产之一。精准地追踪和管理项目工时对于项目的成功至关重要。工时表软件作为一种现代管理工具&#xff0c;其应用不仅简化了项目管理流程&#xff0c;还提高了工作效率&#xff0c;已成为现代企业管理不可或缺的一项利器。 …

[Vue warn]: Invalid vnode type when creating vnode: false

如题&#xff0c;意思是创建vnode时&#xff0c;vnode类型无效:false。 根据右边的索引点进去&#xff0c;发现定位的是组件loading。搜索loading发现声明了变量loading&#xff0c;更改后问题消失。

MySQL数据库高阶语句①

目录 一.按关键字排序 1.单字段排序 &#xff08;1&#xff09;按分数排序 &#xff08;2&#xff09;结合where进行条件筛选 2.多字段排序 &#xff08;1&#xff09;查询学生信息先按兴趣id升序排序&#xff0c;再按id升序排序 &#xff08;2&#xff09;查询信息按兴…

视频素材网站哪个比较好?8个优质视频素材软件app推荐

在探索那些能够让视频作品焕发生机的宝藏网站时&#xff0c;一个好的短视频素材库不仅能提升作品的视觉效果&#xff0c;还能赋予作品更深的情感层次。为了帮助你更好地寻找到这些珍贵的资源&#xff0c;以下是一系列精选的视频素材网站&#xff0c;全面支持你的视频创作需求。…

低照度图像增强算法---传统算法篇

YOLOv8n原图检测YOLOv8n增强后检测召回率和置信度都有提升 前言 这篇博客讲讲低照度,大家都催我出一些内容,没想到这么多同学搞这个,恰好我也做过这方面的一些工作,那今天就来讲解一些方法,低照度的图像增强大体分“传统算法”和“深度学习算法”; 目前低照度的图像增…

linux文本三剑客 --- grep、sed、awk

1、grep grep&#xff1a;使用正则表达式搜索文本&#xff0c;将匹配的行打印出来&#xff08;匹配到的标红&#xff09; 命令格式&#xff1a;grep [option] pattern file <1> 命令参数 -A<显示行数>&#xff1a;除了显示符合范本样式的那一列之外&#xff0c;并…