研读Rust圣经解析——Rust learn-11(测试,迭代器,闭包)

news2025/3/13 7:19:16

研读Rust圣经解析——Rust learn-11(测试,迭代器,闭包)

  • 测试
    • 编写测试模块
    • 声明test模块
    • 编写测试方法
    • 执行测试
    • 测试结果检查
  • 闭包
    • 定义一个闭包
    • 完整写法
    • 闭包可以捕获环境
    • 闭包类比函数
    • 闭包类型推断
    • 闭包获取所有权
    • 将被捕获的值移出闭包和 Fn trait
  • 迭代器
    • 创建一个迭代器
    • 应用迭代器遍历
    • next方法
    • sum方法
    • 迭代器适配器
    • 迭代器的性能

测试

测试我不打算很详细的写,大家知道如何使用其实就差不多了

编写测试模块

一般来说我们在lib中编写测试

cargo new test_01 --lib

这样我们构建了一个test的lib
在这个工程里面你看到应该是有个lib.rs没有main.rs的
在这里插入图片描述

声明test模块

这里并不是声明一个mod,而是一个测试区域,在区域中可以写很多的测试方法

我们通过#[cfg(test)]宏来进行标注

#[cfg(test)]
mod tests {
  
}

编写测试方法

测试方法和普通方法没什么区别,主要在于标注#[test]

 #[test]
    fn test01() {
        assert_eq!(6, 4);
    }

执行测试

我们使用cargo test就可以启动进行测试了,测试会将所有标注#[test]的方法都测试一遍

测试结果检查

我们通常使用assert检查测试结果

  1. assert:断言结果为true,否则panic
  2. assert_eq:断言两边相等,否则panic
  3. assert_ne:断言两边不等,否则panic

闭包

一个可以储存在变量里的类似函数的结构

Rust 的 闭包(closures)是可以保存在一个变量中或作为参数传递给其他函数的匿名函数。可以在一个地方创建闭包,然后在不同的上下文中执行闭包运算。不同于函数,闭包允许捕获被定义时所在作用域中的值。我们将展示闭包的这些功能如何复用代码和自定义行为。

定义一个闭包

如下,我们使用||{}定义了一个闭包,很像是函数的写法:

fn main() {
    let a = || {
        10
    };
    println!("{}",a());
}

这个闭包会返回10,但是实际a的类型是fn:fn->i32,对于但语句来说将{}省略也是OK的

完整写法

|参数|->返回值{
	//...
}

闭包可以捕获环境

如下的闭包中使用到了b变量,但是b没有传入闭包,这就和函数有了差别,使得我们在做一些简单的,不用复用的操作的时候可以直接使用闭包而不是定义一个函数

fn main() {
    let b = 16;
    let a = |x:i32| {
        b * x
    };
    println!("{}",a(10));
}

闭包类比函数

这里给出圣经中的例子:

fn  add_one_v1   (x: u32) -> u32 { x + 1 }
let add_one_v2 = |x: u32| -> u32 { x + 1 };
let add_one_v3 = |x|             { x + 1 };
let add_one_v4 = |x|               x + 1  ;

闭包类型推断

我们看这个程序,看上去没任何问题,但是实际上会报错,原因就是闭包类型推断,前面我们知道闭包会自动推断类型,所以当第一次执行的时候,闭包认为返回值是String,第二次再次调用相同的闭包导致了返回值类型不一致,所以报错:

fn main() {
    let example_closure = |x| x;

    let s = example_closure(String::from("hello"));
    let n = example_closure(5);
}

闭包获取所有权

闭包体不严格需要所有权,如果希望强制闭包获取它用到的环境中值的所有权,可以在参数列表前使用 move 关键字
在将闭包传递到一个新的线程时这个技巧很有用,它可以移动数据所有权给新线程

move ||->返回值类型{
	
}

将被捕获的值移出闭包和 Fn trait

闭包捕获和处理环境中的值的方式影响闭包实现的 trait。Trait 是函数和结构体指定它们能用的闭包的类型的方式。取决于闭包体如何处理值,闭包自动、渐进地实现一个、两个或三个 Fn trait。

  1. FnOnce 适用于能被调用一次的闭包,所有闭包都至少实现了这个 trait,因为所有闭包都能被调用。一个会将捕获的值移出闭包体的闭包只实现 FnOnce trait,这是因为它只能被调用一次。
  2. FnMut 适用于不会将捕获的值移出闭包体的闭包,但它可能会修改被捕获的值。这类闭包可以被调用多次。
  3. Fn 适用于既不将被捕获的值移出闭包体也不修改被捕获的值的闭包,当然也包括不从环境中捕获值的闭包。这类闭包可以被调用多次而不改变它们的环境,这在会多次并发调用闭包的场景中十分重要。

迭代器

迭代器模式允许你对一个序列的项进行某些处理。迭代器(iterator)负责遍历序列中的每一项和决定序列何时结束的逻辑。当使用迭代器时,我们无需重新实现这些逻辑。
迭代器是惰性的:调用方法使用迭代器前无效

创建一个迭代器

调用复合类型的iter方法即可创建,但此时无效

fn main() {
    let arr = vec![123];
    let item_iter = arr.iter();
}

应用迭代器遍历

以下是使用for对迭代器进行遍历

fn main() {
    let mut arr = Vec::new();
    arr.push(1);
    arr.push(2);
    let item_iter = arr.iter();
    for item in item_iter {
        println!("{}",item)
    }
}

使用foreach配合闭包遍历

fn main() {
    let mut arr = Vec::new();
    arr.push(1);
    arr.push(2);
    let item_iter = arr.iter();
    item_iter.for_each(|x|println!("{}",x))
}

通过模拟hasNext进行遍历

fn main() {
    let mut arr = Vec::new();
    arr.push(1);
    arr.push(2);
    let mut item_iter = arr.iter();
    let mut i = item_iter.len();
    while i != 0 {
        println!("{}", item_iter.next().unwrap());
        i = i - 1;
    }
}

next方法

迭代器的next方法返回的是一个Option<T>这样一个对象

sum方法

这个方法一样会消费迭代器,因为获取迭代器的所有权并反复调用 next 来遍历迭代器

let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
let total: i32 = v1_iter.sum();

迭代器适配器

Iterator trait 中定义了另一类方法,被称为 迭代器适配器(iterator adaptors),他们允许我们将当前迭代器变为不同类型的迭代器。可以链式调用多个迭代器适配器。不过因为所有的迭代器都是惰性的,必须调用一个消费适配器方法以便获取迭代器适配器调用的结果。

fn main() {
    let mut arr = Vec::new();
    arr.push(1);
    arr.push(2);
    let new_arr:Vec<_> = arr.iter().map(|x| x + 4).collect();
    println!("{:?}", new_arr);
}

这里我们使用map对迭代器中的每个元素进行操作,最后使用collect收集为新的集合类型

迭代器的性能

实际上迭代器更快,因为迭代器,作为一个高级的抽象,被编译成了与手写的底层代码大体一致性能代码。迭代器是 Rust 的 零成本抽象(zero-cost abstractions)之一,它意味着抽象并不会引入运行时开销
详细大家可以看这里:
https://kaisery.github.io/trpl-zh-cn/ch13-04-performance.html

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

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

相关文章

Jenkins配置邮件通知

1、下载Email Extension插件 2、配置发件人邮箱地址 系统管理 > 系统配置 3、配置邮件通知 系统管理 > 系统配置 > 邮件通知 往下滑找到 通过发送测试邮件测试配置 测试 如果以上配置没有问题,会发送一封测试邮件到服务器中,如果有问题请优先检查一下端口号和是…

网络协议-HTTP协议详情讲解

目录 HTTP协议内容和方法 HTTP请求常见请求头 HTTP常见返回头 HTTP协议基本方法 常见HTTP状态码 面试解惑&#xff1a;301 vs 308 面试解惑&#xff1a;302 / 303 / 307 常见HTTP头 User-Agent Content-Type Origin Accept Referer Connection HTTP协议内容和方法…

Nginx中location规则 与 URL重写(rewrite)详解

1.Nginx中location与rewrite 1.1 location与rewrite常用的正则表达式 符号作用^匹配输入字符串的起始位置$ 匹配输入字符串的结束位置*匹配前面的字符零次或多次。如“ol*”能匹配“o”及“ol”、“oll” 匹配前面的字符一次或多次。如“ol”能匹配“ol”及“oll”、“olll”…

微信为什么使用 SQLite 保存聊天记录

SQLite “只是”一个库&#xff0c;它不是传统意义上的服务器。因此&#xff0c;在某些场合下&#xff0c;它确实不合适。但是&#xff0c;在相当多的其他场合&#xff0c;它却是最合适的选择。SQLite 号称是部署和使用最广泛的数据库引擎。我认为这很有可能&#xff0c;因为 S…

《PyTorch 深度学习实践》第10讲 卷积神经网络(基础篇)

文章目录1 卷积层1.1 torch.nn.Conv2d相关参数1.2 填充&#xff1a;padding1.3 步长&#xff1a;stride2 最大池化层3 手写数字识别该专栏内容为对该视频的学习记录&#xff1a;【《PyTorch深度学习实践》完结合集】 专栏的全部代码、数据集和课件全放在个人GitHub了&#xff…

SpringCloud之OpenFeign介绍案例+相关面试题

概述 OpenFeign是一个声明式的WEB服务客户端&#xff0c;它使WEB服务客户端变得更加容易。具有可插拔的注解支持&#xff0c;SpringCloud中添加了SpringMVC注解的支持。SpringCloud中集成了Ribbon和Eureka&#xff0c;以及SpringCloud LoadBalance&#xff0c;以便在使用Feign时…

C++数据结构:树

树 树是一种数据结构&#xff0c;它是n(n>0)个节点的有限集。n0时称为空树。n>0时&#xff0c;有限集的元素构成一个具有层次感的数据结构。 根 有且仅有一个结点的非空树&#xff0c;那个结点就是根。 A就是上面树的根节点 子树 在一棵非空树中&#xff0c;除根外&a…

由浅入深,一文彻底搞懂Mybatis+面试题分享

mybatis常见面试题链接&#xff1a;2023年-Mybatis常见面试题_是Smoky呢的博客-CSDN博客 MVC架构模式和三层架构 在说Mybatis之前&#xff0c;需要知道MVC架构模式和三层架构的这种思想 MVC架构模式 M&#xff1a;Model&#xff0c;数据层。都是和数据相关&#xff0c;比如实体…

MongoDB实现---事务机制

事务机制 原子性是MongoDB实现事务的难点&#xff0c;隔离性和持久性则是MongoDB事务机制的亮点 ACID支持&#xff1a;由于前面说过MongoDB是基于大数据、提供高度可扩展和高可用&#xff1b;所以其事务机制不仅仅是一般ACID还是结合了BASE理论下的ACID 原子性&#xff1a;保…

键盘录入及标识符

键盘录入 键盘录入介绍&#xff1a; ●为什么要有键盘录入? 目的&#xff1a;为了让我们操作的数据,变得更加灵活 举例&#xff1a;int a10; 这里a虽然是个变量&#xff0c;但记录的值&#xff0c;却是手动写死的。 提问&#xff1a;能不能让a变量记录的值&#xff0c;灵活…

Elasticsearch-mapping

1.Mapping基本概念 Mapping 也称之为映射&#xff0c;定义了 ES 的索引结构、字段类型、分词器等属性&#xff0c;是索引必不可少的组成部分。 ES 中的 mapping 有点类似与RDB中“表结构”的概念&#xff0c;在 MySQL 中&#xff0c;表结构里包含了字段名称&#xff0c;字段的…

# IMAGE - Image Perimeters

# IMAGE - Image Perimeters ## 题面翻译 ### 描述 给出一张由"x"和"."组成的矩阵。每个"x"可以向上下左右及两个斜对角进行连通&#xff0c;请问由某个点开始的"x"&#xff0c;它所连通的图形的周长为多少。 ### 输入 整个测试有多…

SpringBoot 整合ChatGPT API项目实战

准备工作 &#xff08;1&#xff09;已成功注册 OpenAI 的账号。 &#xff08;2&#xff09;创建 API KEY&#xff0c;这个 API KEY 是用于 HTTP 请求身份验证的&#xff0c;可以创建多个。 注意这个创建之后需要马上复制好保存&#xff0c;关闭弹框之后就看不到了。 &#xf…

excel的导入导出的两种方案 (1 EasyExcel 2 Hutool工具类)

文章目录 前言1 EasyExcel的导入导出导出1 导入依赖2 项目中的CourseEntity实体类3 CoureseVo VO类 (对CourseEntity进行EasyExcel导入导出操作)4 导出代码的编写 并最终测试导出效果5 (前端内容 可选)通过vue按钮点击 导出 导入1 导入依赖 跟导出相同2 创建回调监听器3 编写导…

1、Typescript基础入门与环境搭建

1、开发工具安装与基本配置 1.1、软件下载安装 如果你还没有使用过VSCode&#xff0c;当然先要去官网下载了。下载完成后双击安装&#xff0c;一直下一步即可。 1.2、编辑器汉化 如果你英语不是很好&#xff0c;配置中文版界面是很有必要的&#xff0c;安装个插件就可以了。打…

虚化背景 - 基于镜头模糊滤镜的深度映射

镜头模糊 Lens Blur等滤镜可以使用深度映射 Depth Map来设置像素在视觉上的前后关系。因此&#xff0c;常利用深度映射来创建真实感虚化效果&#xff0c;或者进行超越镜头的任意虚化处理。 ◆ ◆ ◆ 基于 Alpha 通道的深度映射关系 一般可通过建立 Alpha 通道或图层蒙版来创建…

【算法基础】DP第三弹 —— 竞赛篇

一、计数问题 (一)Question 1. 问题描述 2. Input 输入包含多组测试数据。每组测试数据占一行,包含两个整数 a 和 b。当读入一行为 0 0 时,表示输入终止,且该行不作处理。(0 < a, b < 100000000) 3. Output 每组数据输出一个结果,每个结果占一行。每个结果包…

MIPI D-PHYv2.5笔记(21) -- Forward High-Speed Data Transmission Timing

声明&#xff1a;作者是做嵌入式软件开发的&#xff0c;并非专业的硬件设计人员&#xff0c;笔记内容根据自己的经验和对协议的理解输出&#xff0c;肯定存在有些理解和翻译不到位的地方&#xff0c;有疑问请参考原始规范看 DDR时钟差分信号和Data差分信号的时序关系如下图所示…

计及调度经济性的光热电站储热容量配置方法【IEEE30节点】(Matlab代码实现)

&#x1f4a5; &#x1f4a5; &#x1f49e; &#x1f49e; 欢迎来到本博客 ❤️ ❤️ &#x1f4a5; &#x1f4a5; &#x1f3c6; 博主优势&#xff1a; &#x1f31e; &#x1f31e; &#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 …

计算机网络简史

ARPANET的发展 互联网最早的雏形 1931-ARPANET设计 互联网名人堂 1965-packet switching(分包交换) 1969 第一个RFC(Request for Comments)(开始通过APPANET发布)第一个接口信息处理单元&#xff08;Interface Message Processor&#xff09;&#xff08;下图&#xff0c;节…