一、分支
(一)if
1.if
语法格式
if boolean_expression {
}
例子
fn main(){let num:i32 = 5;if num > 0 {println!("正数");}
}
条件表达式不需要用小括号。
条件表达式必须是bool类型。
2.if else
语法格式
if boolean_expression {
} else {
}
例子
fn main() {let num = 12;if num % 2==0 {println!("偶数");} else {println!("奇数");}
}
if else既可以作语句,又可以作表达式。当作表达式时,它的值是其分支代码块最后一个表达式的值。
我们可以在 let 语句的右侧使用它,例如:
fn main() {let condition = true;let number = if condition { 5 } else { 6 };println!("The value of number is: {number}");
}
值取决于哪个代码块被执行。这意味着 if 的每个分支的可能的返回值都必须是相同类型;
在上例中,if 分支和 else 分支的结果都是 i32 整型。如果它们的类型不匹配,如下面这个例子,则会出现一个错误:
fn main() {let condition = true;let number = if condition { 5 } else { "six" };println!("The value of number is: {number}");
}
3.if else if
语法格式
if boolean_expression1 {
} else if boolean_expression2 {
} else {
}
例子
fn main() {let num = 2 ;if num > 0 {println!("{} is positive",num);} else if num < 0 {println!("{} is negative",num);} else {println!("{} is neither positive nor negative",num) ;}
}
(二)if let
句法
IfLetExpression :
if let Pattern = Expression BlockExpression
(else ( BlockExpression | IfExpression | IfLetExpression ) )?
1.if let
语法格式
if let Pattern = Expression {
}
Pattern就是模式。
如果Pattern与Expression匹配,就执行相应的代码块。
可以使用操作符 | 指定多个模式。 这与match表达式中的 | 具有相同的语义
例子
enum E {X(u8),Y(u8),Z(u8),
}
let v = E::Y(12);
if let E::X(n) | E::Y(n) = v {assert_eq!(n, 12);
}
2.if let else
语法格式
if let Pattern = Expression {
} else {
}
例子
let dish = ("Ham", "Eggs");
if let ("Bacon", b) = dish {println!("Bacon is served with {}", b);
} else {println!("No bacon will be served");
}
if let else与if else一样既可以作语句,又可以作表达式。当作表达式时,它的值是其分支代码块最后一个表达式的值。
fn main() {let x = Some(3);let a = if let Some(1) = x { 1 } else { 5 };println!("{a}");
}
3.if let else if let
语法格式
if let Pattern1 = Expression1 {
} else if let Pattern2 = Expression2 {
}else {
}
4.if和if let混合使用
语法格式
if let Pattern = Expression {
} else if boolean_expression{
}else {
}
例子
let x = Some(3);
let a = if let Some(1) = x {1
} else if x == Some(2) {2
} else if let Some(y) = x {y
} else {-1
};
assert_eq!(a, 3);
5.if let等价于match
例如:
if let PATS = EXPR {/* body */
} else {/*else */
}
等价于
match EXPR {PATS => { /* body */ },_ => { /* else */ }, // 如果没有else块,这相当于 `()`
}
在一些场合下,用match并不优雅,因为match必须考虑所有可能的值。
比如:
let optional = Some(7);
match optional {Some(i) => {println!("This is a really long string and `{:?}`", i);},_ => {}, // 必须有,因为 `match` 需要覆盖全部情况。不觉得这行很多余吗?
};
if let在这样的场合要简洁得多
fn main() {let number = Some(7);if let Some(i) = number {println!("Matched {:?}!", i);}
}
另一个好处是:if let允许匹配枚举非参数化的变量,即枚举未注明 #[derive(PartialEq)],我们也没有为其实现PartialEq。在这种情况下,通常if Foo::Bar==a会出错,因为此类枚举的实例不具有可比性。但是,if let是可行的。
你想挑战一下吗?使用if let修复以下示例:
// 该枚举故意未注明 `#[derive(PartialEq)]`,
// 并且也没为其实现 `PartialEq`。这就是为什么下面比较 `Foo::Bar==a` 会失败的原因。
enum Foo {Bar}fn main() {let a = Foo::Bar;// 变量匹配Foo::Barif Foo::Bar == a {// ^-- 这就是编译时发现的错误。使用 `if let` 来替换它。println!("a is foobar");}
}
(三)match
match用于检查值是否匹配一组模式中的某一个。似于C语言中的 switch 语句
语法格式
match variable_expression {pattern1 => {},pattern2 => {// },_ => {// 默认}
};
例子
fn main() {let x = 1;match x {1 => println!("one"),2 => println!("two"),3 => println!("three"),4 => println!("four"),5 => println!("five"),_ => println!("something else"),}
}
match既可以作语句,也可以作表达式,作表达式时,它把匹配分支代码块的最后一条表达式的结果当作返回值。
例子
fn main(){let state_code = "MH";let state = match state_code {"MH" => "Maharashtra","KL" => "Kerala","KA" => "Karnadaka","GA" => "Goa",_ => "Unknown"};println!("State name is {}",state);
}
运行结果
State name is Maharashtra
模式守卫
守卫出现在模式的后面,由关键字if后面的布尔类型表达式组成。
当模式匹配成功时,将执行守卫表达式。 只有此表达式的计算结果为真,才认为完全匹配成功。 否则,匹配将测试下一个模式,包括测试同一分支中运算符 | 分割的后续模式。
fn main() {let maybe_digit = Some(8);match maybe_digit {Some(x) if x < 10 => println!("digit < 10"),Some(x) => println!("digit >= 10"),None => panic!(),};
}
注意:使用操作符 | 的分支可能会导致后跟的守卫必须多次执行的副作用。 例如:
use std::cell::Cell;
let i = Cell::new(0i32);
match 1 {1 | _ if { i.set(i.get() + 1); false } => {}_ => {}
}
assert_eq!(i.get(), 2);
二、循环
(一)for
Rust 中的 for 只有 for in 这种格式,常用于遍历容器的元素
句法
IteratorLoopExpression :
for Pattern in Expression BlockExpression
pattern就是模式
例子
下面的代码,使用 for…in 循环,重复输出1到11之间的数字(不包括11)
fn main(){for x in 1..11{println!("x is {}",x);}
}
fn main() {let a = [10, 20, 30, 40, 50];for element in a {println!("the value is: {element}");}
}
(二)while
句法
PredicateLoopExpression :
while Expression BlockExpression
例子
下面的代码,使用 while 循环重写下上面的代码,重复输出1到11之间的数字(不包括11)
fn main(){let mut x = 1;while x < 11{println!("inside loop x value is {}",x);x+=1;}println!("outside loop x value is {}",x);
}
(三)loop
loop 语句代表着一种死循环。
语法格式
loop {
}
范例
下面的语句,我们使用 loop 输出1到无限大的数字。
fn main(){let mut x = 0;loop {x+=1;println!("x={}",x);}
}
(四)while let
句法
PredicatePatternLoopExpression :
while let Pattern = Expression BlockExpression
Pattern就是模式
如果值与模式匹配,则执行循环体块。如果不匹配,则跳出循环。
可以使用操作符 | 指定多个模式。
let mut vals = vec![2, 3, 1, 2, 2];
while let Some(v @ 1) | Some(v @ 2) = vals.pop() {// 打印2, 2, 然后1println!("{}", v);
}
例子
let mut x = vec![1, 2, 3];
while let Some(y) = x.pop() {println!("y = {}", y);
}
while let _ = 5 {println!("不可反驳模式总是会匹配成功");break;
}
while let等价于包含match的loop。
如下:
while let PATS = EXPR {/* loop body */
}
等价于
loop {match EXPR {PATS => { /* loop body */ },_ => break,}
}
三、循环标签
句法
LoopLabel :
LIFETIME_OR_LABEL :
一个循环表达式可以选择设置一个标签。这类标签被标记为循环表达式之前的生存期(标签),如 'foo: loop { break 'foo; }、'bar: while false {}、'humbug: for _ in 0…0 {}。 如果循环存在标签,则嵌套在该循环中的带此标签的break表达式和continue表达式可以退出此标签标记的循环层或将控制流返回至此标签标记的循环层的头部。
四、跳出循环
(一)break
句法
BreakExpression :
break LIFETIME_OR_LABEL? Expression?
当遇到break时,相关的循环体的执行将立即结束,例如:
let mut last = 0;
for x in 1..100 {if x > 12 {break;}last = x;
}
assert_eq!(last, 12);
break表达式只能跳出一层循环,如果要跳出多层循环,就要使用循环标签。
例如:
'outer: loop {while true {break 'outer;}
}
break表达式只允许在循环体内使用,它有break、break 'label、break EXPR、break 'label EXPR这四种形式。
fn main(){let mut x = 0;loop {x+=1;if x > 10 {break;}println!("x={}",x);}
}
break 可以返回值
当使用loop循环时,可以使用break表达式从循环中返回一个值,通过形如break EXPR或break 'label EXPR来返回,其中EXPR是一个表达式。
其后不跟表达式的break与后跟 () 的break效果相同。
例如:
let (mut a, mut b) = (1, 1);
let result = loop {if b > 10 {break b;}let c = a + b;a = b;b = c;
};
// 斐波那契数列中第一个大于10的值:
assert_eq!(result, 13);
(二)continue
句法
ContinueExpression :
continue LIFETIME_OR_LABEL?
当遇到continue时,相关的循环体的当前迭代将立即结束,并将控制流返回到循环头。 在while循环的情况下,循环头是控制循环的条件表达式。 在for循环的情况下,循环头是控制循环的调用表达式。
与break一样,continue只能跳过一层循环,如果要跳过多层,可以使用continue 'label。
continue表达式只允许在循环体内部使用。
fn main(){for x in 1..11{if 5 == x {continue;}println!("x is {}",x);}
}