seo网站优化及网站推广百度网址大全下载安装
文章目录
- 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
允许匹配一种模式,而忽略其余的模式( 可驳模式匹配 )。