【Rust基础②】流程控制、模式匹配
文章目录
- 4 流程控制
- 4.1 if else表达式
- 4.2 循环控制
- 4.2.1 for循环
- 4.2.2 while循环
- 4.2.3 loop循环
- 5 模式匹配
- 5.1 match和if let
- 5.1.1 match匹配
- 使用match表达式赋值
- 模式绑定
- `_`通配符
- 5.1.2 if let 匹配
- 5.1.3 matches! 宏
- 5.2 解构Option
- 5.3 认识模式
- match 分支
- if let 分支
- while let 条件循环
- for 循环
- let 语句
- 函数参数
- let 和 if let
- 5.3 [全模式列表](https://course.rs/basic/match-pattern/all-patterns.html)
4 流程控制
4.1 if else表达式
if condition == true {// A...
} else {// B...
}
该代码读作:若 condition 的值为 true,则执行 A 代码,否则执行 B 代码。例如:
fn main() {let condition = true;let number = if condition {5} else {6};println!("The value of number is: {}", number);
}
if语句块是表达式,这里我们使用if表达式的返回值来给number进行赋值:number的值是5- 用
if来赋值时,要保证每个分支返回的类型一样(事实上,这种说法不完全准确,见这里),此处返回的5和6就是同一个类型,如果返回类型不一致就会报错
可以将 else if 与 if、else 组合在一起实现更复杂的条件分支判断
4.2 循环控制
4.2.1 for循环
核心就在于 for 和 in 的联动,语义表达如下:
for 元素 in 集合 {// 使用元素干一些你懂我不懂的事情
}
例如:
fn main() {for i in 1..=5 {println!("{}", i);}
}
以上代码循环输出一个从 1 到 5 的序列。
注意,使用 for 时我们往往使用集合的引用形式,除非你不想在后面的代码中继续使用该集合(比如我们这里使用了 container 的引用)。如果不使用引用的话,所有权会被转移(move)到 for 语句块中,后面就无法再使用这个集合了)
如果想在循环中,修改该元素,可以使用 mut 关键字:
for item in &mut collection {// ...
}
总结如下:
| 使用方法 | 等价使用方式 | 所有权 |
|---|---|---|
for item in collection | for item in IntoIterator::into_iter(collection) | 转移所有权 |
for item in &collection | for item in collection.iter() | 不可变借用 |
for item in &mut collection | for item in collection.iter_mut() | 可变借用 |
如果想在循环中获取元素的索引:
fn main() {let a = [4, 3, 2, 1];// `.iter()` 方法把 `a` 数组变成一个迭代器for (i, v) in a.iter().enumerate() {println!("第{}个元素是{}", i + 1, v);}
}
两种循环方式:
// 第一种
let collection = [1, 2, 3, 4, 5];
for i in 0..collection.len() {let item = collection[i];// ...
}// 第二种
for item in collection {}
第一种方式是循环索引,然后通过索引下标去访问集合,第二种方式是直接循环集合中的元素,优劣如下:
- 性能:第一种使用方式中
collection[index]的索引访问,会因为边界检查(Bounds Checking)导致运行时的性能损耗 —— Rust 会检查并确认index是否落在集合内,但是第二种直接迭代的方式就不会触发这种检查,因为编译器会在编译时就完成分析并证明这种访问是合法的 - 安全:第一种方式里对
collection的索引访问是非连续的,存在一定可能性在两次访问之间,collection发生了变化,导致脏数据产生。而第二种直接迭代的方式是连续访问,因此不存在这种风险
使用 continue 可以跳过当前当次的循环,开始下次的循环;使用 break 可以直接跳出当前整个循环
4.2.2 while循环
如果你需要一个条件来循环,当该条件为 true 时,继续循环,条件为 false,跳出循环,那么 while 就非常适用:
fn main() {let mut n = 0;while n <= 5 {println!("{}!", n);n = n + 1;}println!("我出来了!");
}
4.2.3 loop循环
loop 就是一个简单的无限循环,你可以在内部实现逻辑通过 break 关键字来控制循环何时结束。当使用 loop 时,必不可少的伙伴是 break 关键字,它能让循环在满足某个条件时跳出:
fn main() {let mut counter = 0;let result = loop {counter += 1;if counter == 10 {break counter * 2;}};println!("The result is {}", result);
}
以上代码当 counter 递增到 10 时,就会通过 break 返回一个 counter * 2 的值,最后赋给 result 并打印出来。
这里有几点值得注意:
- break 可以单独使用,也可以带一个返回值,有些类似
return - loop 是一个表达式,因此可以返回一个值
5 模式匹配
5.1 match和if let
5.1.1 match匹配
match 的通用形式:
match target {模式1 => 表达式1,模式2 => {语句1;语句2;表达式2},_ => 表达式3
}
该形式清晰的说明了何为模式,何为模式匹配:将模式与 target 进行匹配,即为模式匹配
match的匹配必须要穷举出所有可能,因此这里用_来代表未列出的所有可能性match的每一个分支都必须是一个表达式,且所有分支的表达式最终返回值的类型必须相同
使用match表达式赋值
match 本身也是一个表达式,因此可以用它来赋值:
enum IpAddr {Ipv4,Ipv6
}fn main() {let ip1 = IpAddr::Ipv6;let ip_str = match ip1 {IpAddr::Ipv4 => "127.0.0.1",_ => "::1",};println!("{}", ip_str);
}
因为这里匹配到 _ 分支,所以将 "::1" 赋值给了 ip_str。
模式绑定
模式匹配的一个重要功能是从模式中取出绑定的值。
模式绑定的例子:
enum Action {Say(String),MoveTo(i32, i32),ChangeColorRGB(u16, u16, u16),
}fn main() {let actions = [Action::Say("Hello Rust".to_string()),Action::MoveTo(1,2),Action::ChangeColorRGB(255,255,0),];for action in actions {match action {Action::Say(s) => {println!("{}", s);},Action::MoveTo(x, y) => {println!("point from (0, 0) move to ({}, {})", x, y);},Action::ChangeColorRGB(r, g, _) => {println!("change color into '(r:{}, g:{}, b:0)', 'b' has been ignored",r, g,);}}}
}
运行后输出:
$ cargo runCompiling world_hello v0.1.0 (/Users/sunfei/development/rust/world_hello)Finished dev [unoptimized + debuginfo] target(s) in 0.16sRunning `target/debug/world_hello`
Hello Rust
point from (0, 0) move to (1, 2)
change color into '(r:255, g:255, b:0)', 'b' has been ignored
_通配符
match 的匹配必须穷尽所有情况,即穷尽匹配,Rust 编译器清晰地知道 match 中有哪些分支没有被覆盖, 这种行为能强制我们处理所有的可能性。
通过将 _ 其放置于其他分支后,_ 将会匹配所有遗漏的值。() 表示返回单元类型与所有分支返回值的类型相同,所以当匹配到 _ 后,什么也不会发生。
5.1.2 if let 匹配
有时会遇到只有一个模式的值需要被处理,其它值直接忽略的场景,如果用 match 来处理就要写成下面这样:
let v = Some(3u8);match v {Some(3) => println!("three"),_ => (),}
我们只想要对 Some(3) 模式进行匹配, 不想处理任何其他 Some<u8> 值或 None 值。但是为了满足 match 表达式(穷尽性)的要求,写代码时必须在处理完这唯一的成员后加上 _ => (),这样会增加不少无用的代码。
俗话说“杀鸡焉用牛刀”,我们完全可以用 if let 的方式来实现:
if let Some(3) = v {println!("three");
}
这两种匹配对于新手来说,可能有些难以抉择,但是只要记住一点就好:当你只要匹配一个条件,且忽略其他条件时就用 if let ,否则都用 match。
无论是 match 还是 if let,他们都可以在模式匹配时覆盖掉老的值,绑定新的值:
fn main() {let age=Some(30);let b=3;println!("匹配前b:{:?}",b); //3if let b=age{println!("b:{:?}",b); //Some(30)}println!("匹配后b:{:?}",b); //3
}
5.1.3 matches! 宏
matches!宏可以将一个表达式跟模式进行匹配,然后返回匹配的结果 true or false。
例如:
let foo = 'f';
assert!(matches!(foo, 'A'..='Z' | 'a'..='z'));let bar = Some(4);
assert!(matches!(bar, Some(x) if x > 2));
5.2 解构Option
Opention枚举用来解决Rust中变量是否有值的问题,定义如下:
enum Option<T> {Some(T),None,
}
简单解释就是:一个变量要么有值:Some(T), 要么为空:None。
使用 Option<T>,是为了从 Some 中取出其内部的 T 值以及处理没有值的情况,为了演示这一点,下面一起来编写一个函数,它获取一个 Option<i32>,如果其中含有一个值,将其加一;如果其中没有值,则函数返回 None 值:
fn plus_one(x: Option<i32>) -> Option<i32> {match x {None => None,Some(i) => Some(i + 1),}
}let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
plus_one 接受一个 Option<i32> 类型的参数,同时返回一个 Option<i32> 类型的值(这种形式的函数在标准库内随处所见),在该函数的内部处理中,如果传入的是一个 None ,则返回一个 None 且不做任何处理;如果传入的是一个 Some(i32),则通过模式绑定,把其中的值绑定到变量 i 上,然后返回 i+1 的值,同时用 Some 进行包裹。
5.3 认识模式
模式是 Rust 中的特殊语法,它用来匹配类型中的结构和数据,模式一般由以下内容组合而成:
- 字面值
- 解构的数组、枚举、结构体或者元组
- 变量
- 通配符
- 占位符
所有可能认识到模式的地方:
match 分支
match VALUE {PATTERN => EXPRESSION,PATTERN => EXPRESSION,PATTERN => EXPRESSION,
}
如上所示,match 的每个分支就是一个模式,因为 match 匹配是穷尽式的,因此我们往往需要一个特殊的模式 _,来匹配剩余的所有情况:
match VALUE {PATTERN => EXPRESSION,PATTERN => EXPRESSION,_ => EXPRESSION,
}
if let 分支
if let 往往用于匹配一个模式,而忽略剩下的所有模式的场景:
if let PATTERN = SOME_VALUE {}
while let 条件循环
一个与 if let 类似的结构是 while let 条件循环,它允许只要模式匹配就一直进行 while 循环。下面展示了一个使用 while let 的例子:
// Vec是动态数组
let mut stack = Vec::new();// 向数组尾部插入元素
stack.push(1);
stack.push(2);
stack.push(3);// stack.pop从数组尾部弹出元素
while let Some(top) = stack.pop() {println!("{}", top);
}
这个例子会打印出 3、2 接着是 1。pop 方法取出动态数组的最后一个元素并返回 Some(value),如果动态数组是空的,将返回 None,对于 while 来说,只要 pop 返回 Some 就会一直不停的循环。一旦其返回 None,while 循环停止。可以使用 while let 来弹出栈中的每一个元素。
for 循环
let v = vec!['a', 'b', 'c'];for (index, value) in v.iter().enumerate() {println!("{} is at index {}", value, index);
}
这里使用 enumerate 方法产生一个迭代器,该迭代器每次迭代会返回一个 (索引,值) 形式的元组,然后用 (index,value) 来匹配。
let 语句
let PATTERN = EXPRESSION;
是的, 该语句我们已经用了无数次了,它也是一种模式匹配:
let x = 5;
这其中,x 也是一种模式绑定,代表将匹配的值绑定到变量 x 上。因此,在 Rust 中,变量名也是一种模式,只不过它比较朴素很不起眼罢了。
let (x, y, z) = (1, 2, 3);
上面将一个元组与模式进行匹配(模式和值的类型必需相同!),然后把 1, 2, 3 分别绑定到 x, y, z 上。
模式匹配要求两边的类型必须相同,否则就会导致下面的报错:
let (x, y) = (1, 2, 3);
error[E0308]: mismatched types--> src/main.rs:4:5|
4 | let (x, y) = (1, 2, 3);| ^^^^^^ --------- this expression has type `({integer}, {integer}, {integer})`| || expected a tuple with 3 elements, found one with 2 elements|= note: expected tuple `({integer}, {integer}, {integer})`found tuple `(_, _)`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to previous error
对于元组来说,元素个数也是类型的一部分!
函数参数
函数参数也是模式:
fn foo(x: i32) {// 代码
}
其中 x 就是一个模式,你还可以在参数中匹配元组:
fn print_coordinates(&(x, y): &(i32, i32)) {println!("Current location: ({}, {})", x, y);
}fn main() {let point = (3, 5);print_coordinates(&point);
}
&(3, 5) 会匹配模式 &(x, y),因此 x 得到了 3,y 得到了 5。
let 和 if let
对于以下代码,编译器会报错:
let Some(x) = some_option_value;
因为右边的值可能不为 Some,而是 None,这种时候就不能进行匹配,也就是上面的代码遗漏了 None 的匹配。
类似 let , for和match 都必须要求完全覆盖匹配,才能通过编译( 不可驳模式匹配 )。
但是对于 if let,就可以这样使用:
if let Some(x) = some_option_value {println!("{}", x);
}
因为 if let 允许匹配一种模式,而忽略其余的模式( 可驳模式匹配 )。
5.3 全模式列表
相关文章:
【Rust基础②】流程控制、模式匹配
文章目录 4 流程控制4.1 if else表达式4.2 循环控制4.2.1 for循环4.2.2 while循环4.2.3 loop循环 5 模式匹配5.1 match和if let5.1.1 match匹配使用match表达式赋值模式绑定_通配符 5.1.2 if let 匹配5.1.3 matches! 宏 5.2 解构Option5.3 认识模式match 分支if let 分支while …...
Qt出现假死冻结现象
应用程序出现假死或冻结现象通常是由于一些常见问题所导致的。下面是一些可能的原因和解决方法: 长时间运行的任务在主线程中执行: 如果您在主线程中执行了长时间运行的任务,如文件操作、网络请求或复杂的计算,这可能导致应用程序…...
XML外部实体注入攻击XXE
xml是扩展性标记语言,来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素,一般无法直接打开,可以选择用excl或记事本打…...
Hudi第三章:集成Flink
系列文章目录 Hudi第一章:编译安装 Hudi第二章:集成Spark Hudi第二章:集成Spark(二) Hudi第三章:集成Flink 文章目录 系列文章目录前言一、环境准备1.上传并解压2.修改配置文件3.拷贝jar包4.启动sql-client1.启动hadoop2.启动ses…...
MTC证书|欧盟与英国金属类产品清关新要求
从10月1日起,欧盟海关将严格检查所有申报HS代码为7323、7326等含有金属的货物,所有进口国家的金属相关产品必须提供MTC证书,证明产品材料的来源并非源自俄罗斯。 对于未使用7323、7326等含有金属类的HS编码申报,且品名未明显体现…...
保护敏感数据的艺术:数据安全指南
多年来,工程和技术迅速转型,生成和处理了大量需要保护的数据,因为网络攻击和违规的风险很高。为了保护企业数据,组织必须采取主动的数据安全方法,了解保护数据的最佳实践,并使用必要的工具和平台来实现数据…...
Commonjs与ES Module
commonjs 1 commonjs 实现原理 commonjs每个模块文件上存在 module,exports,require三个变量,然而这三个变量是没有被定义的,但是我们可以在 Commonjs 规范下每一个 js 模块上直接使用它们。在 nodejs 中还存在 __filename 和 __dirname 变…...
分布式对象存储
参考《分布式对象存储----原理、架构以及Go语言实现》(作者:胡世杰) 对象存储简介 数据的管理方式 以对象的方式管理数据,一个对象包括:对象的数据、对象的元数据、对象的全局唯一标识符 访问数据的方式 可扩展的分…...
跨境独立站代购中国电商平台商品PHP多语言多货币
跨境独立站代购中国电商平台商品是指代购者在海外建立自己的独立电商平台,代理中国主流电商平台(如淘宝、京东等)的商品进行销售和代购。这种模式的优势在于代购者可以自主选择产品和价格策略,同时还能提供更专业和优质的服务。 …...
Python接口自动化 —— Json 数据处理实战(详解)
简介 上一篇说了关于json数据处理,是为了断言方便,这篇就带各位小伙伴实战一下。首先捋一下思路,然后根据思路一步一步的去实现和实战,不要一开始就盲目的动手和无头苍蝇一样到处乱撞,撞得头破血流后而放弃了。不仅什么…...
微信页面公众号页面 安全键盘收起后页面空白
微信浏览器打开H5页面和公众号页面,输入密码时调起安全键盘,键盘收起后 键盘下方页面留白 解决办法: 1、(简单)只有在调起安全键盘(输入密码)的时候会出现这种情况,将input属性改为n…...
数据结构 - 二叉树
递归实现前中后序遍历 #include<stdio.h> #include<stdlib.h>#define TElemType inttypedef struct BiTNode{TElemType data;struct BiTNode *lchild,*rchild; }BiTNode,*BiTree; BiTNode root;void visit(TElemType& e){printf("%d",e); }void Pre…...
【Overload游戏引擎细节分析】从视图投影矩阵提取视锥体及overload对视锥体的封装
overoad代码中包含一段有意思的代码,可以从视图投影矩阵逆推出摄像机的视锥体,本文来分析一下原理 一、平面的方程 视锥体是用平面来表示的,所以先看看平面的数学表达。 平面方程可以由其法线N(A, B, C)和一个点Q(x0,…...
Linux 安全 - LSM hook点
文章目录 一、LSM file system hooks1.1 LSM super_block hooks1.2 LSM file hooks1.3 LSM inode hooks 二、LSM Task hooks三、LSM IPC hooks四、LSM Network hooks五、LSM Module & System hooks 一、LSM file system hooks 在VFS(虚拟文件系统)层…...
【iOS逆向与安全】越狱检测与过检测附ida伪代码
首先在网上查找一些检测代码 放入项目运行,用 ida 打开后 F5 得到下面的 __int64 __usercall sub_10001B3F0<X0>(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6, __int64 a7, __int64 a8, __int64 a9, __int64 a10, __int64 a11…...
Android Studio gradle手动下载配置
项目同步时,有时候会遇到Android Studio第一步下载gradle就是连接失败的问题。 这种情况,我们可以手动去gradle官网下载好gradle文件,放置在Android Studio的缓存目录下,这样AS在同步代码时就会自动解压下载好的文件。 步骤如下&…...
ChatGPT Prompting开发实战(十三)
一. 如何评估prompts是否包含有害内容 用户在与ChatGPT交互时提供的prompts可能会包括有害内容,这时可以通过调用OpenAI提供的API来进行判断,接下来给出示例,通过调用模型“gpt-3.5-turbo”来演示这个过程。 prompt示例如下&…...
银河麒麟 ARM 架构 离线安装Docker
1. 下载对应的安装包 进入此地址下载对应的docker 离线安装包 下载地址 将文件上传到服务器 解压此文件 tar zxf docker-18.09.1.tgz将 docker 相关命令拷贝到 /usr/bin,方便直接运行命令 cp docker/* /usr/bin/启动Docker守护程序 dockerd &验证是否安装成…...
虹科科技 | 探索CAN通信世界:PCAN-Explorer 6软件的功能与应用
CAN(Controller Area Network)总线是一种广泛应用于汽车和工业领域的通信协议,用于实时数据传输和设备之间的通信。而虹科的PCAN-Explorer 6软件是一款功能强大的CAN总线分析工具,为开发人员提供了丰富的功能和灵活性。本文将重点…...
SELECT COUNT(*)会不会导致全表扫描引起慢查询
SELECT COUNT(*)会不会导致全表扫描引起慢查询呢? SELECT COUNT(*) FROM SomeTable 网上有一种说法,针对无 where_clause 的 COUNT(*),MySQL 是有优化的,优化器会选择成本最小的辅助索引查询计数,其实反而性能最高&…...
css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
