流程控制结构
流程控制结构包括:
- if条件判断结构
- loop循环
- while循环
- for..in迭代
除此之外,还有其他几种本节暂不介绍的控制结构。
需要说明的是,Rust中这些结构都是表达式,它们都有默认的返回值(),且if结构和loop循环结构可以指定返回值。
注:【这些结构的默认返回值是
()】的说法是不严谨的之所以可以看作是默认返回
(),是因为Rust会在每个分号结尾的语句后自动加上小括号(),使得语句看上去也有了返回值。为了行文简洁,下文将直接描述为默认返回值。
if..else
if语句的语法如下:
#![allow(unused)]
fn main() {
if COND1 {
...
} else if COND2 {
...
} else {
...
}
}
其中,条件表达式COND不需要加括号,且COND部分只能是布尔值类型。另外,else if分支是可选的,且可以有多个,else分支也是可选的,但最多只能有一个。
由于if结构是表达式,它有返回值,所以可以将if结构赋值给一个变量(或者其他需要值的地方)。
但是要注意,if结构默认返回Unit类型的(),这个返回值是没有意义的。如果要指定为其他有意义的返回值,要求:
- 分支最后执行的那一行代码不使用分号结尾,这表示将最后执行的这行代码的返回值作为if结构的返回值
- 每个分支的返回值类型相同,这意味着每个分支最后执行的代码都不能使用分号结尾
- 必须要有else分支,否则会因为所有分支条件判断都不通过而直接返回if的默认返回值
()
下面用几个示例来演示这几个要求。
首先是一段正确的代码片段:
#![allow(unused)]
fn main() {
let x = 33;
// 将if结构赋值给变量a
// 下面if的每个分支,其返回值类型都是i32类型
let a = if x < 20 {
// println!()不是该分支最后一条语句,要加结尾分号
println!("x < 20");
// x+10是该分支最后一条语句,
// 不加分号表示将其计算结果返回,返回类型为i32
x + 10
} else if x < 30 {
println!("x < 30");
x + 5 // 返回x + 5的计算结果,返回类型为i32
} else {
println!("x >= 30");
x // 直接返回x,返回类型为i32
}; // if最后一个闭大括号后要加分号,这是let的分号
}
下面是一段将if默认返回值()赋值给变量的代码片段:
#![allow(unused)]
fn main() {
let x = 33;
// a被赋值为`()`
let a = if x < 20 {
println!("x < 20");
};
println!("{:?}", a); // ()
}
下面不指定else分支,将报错:
#![allow(unused)]
fn main() {
let x = 33;
// if分支返回i32类型的值
// 但如果没有执行if分支,则返回默认值`()`
// 这使得a的类型不是确定的,因此报错
let a = if x < 20 {
x + 3 // 该分支返回i32类型
};
}
下面if分支和else if分支返回不同类型的值,将报错:
#![allow(unused)]
fn main() {
let x = 33;
let a = if x < 20 {
x + 3 // i32类型
} else if x < 30 {
"hello".to_string() // String类型
} else {
x // i32类型
};
}
由于if的条件表达式COND部分要求必须是布尔值类型,因此不能像其他语言一样编写类似于if "abc" {}这样的代码。但是,却可以在COND部分加入其他语句,只要保证COND部分的返回值是bool类型即可。
例如下面的代码。注意下面使用大括号{}语句块包围了if的COND部分,使得可以先执行其他语句,在语句块的最后才返回bool值作为if的分支判断条件。
#![allow(unused)]
fn main() {
let mut x = 0;
if {x += 1; x < 3} {
println!("{}", x);
}
}
这种用法在if结构上完全是多此一举的,但COND的这种用法也适用于while循环,有时候会有点用处。
while循环
while循环的语法很简单:
#![allow(unused)]
fn main() {
while COND {
...
}
}
其中,条件表达式COND和if结构的条件表达式规则完全一致。
如果要中途退出循环,可使用break关键字,如果要立即进入下一轮循环,可使用continue关键字。
例如:
#![allow(unused)]
fn main() {
let mut x = 0;
while x < 5 {
x += 1;
println!("{}", x);
if x % 2 == 0 {
continue;
}
}
}
根据前文对if的条件表达式COND的描述,COND部分允许加入其他语句,只要COND部分最后返回bool类型即可。例如:
#![allow(unused)]
fn main() {
let mut x = 0;
// 相当于do..while
while {println!("{}", x);x < 5} {
x += 1;
if x % 2 == 0 {
continue;
}
}
}
最后,while虽然有默认返回值(),但()作为返回值是没有意义的。因此,不考虑while的返回值问题。
loop循环
loop表达式是一个无限循环结构。只有在loop循环体内部使用break才能终止循环。另外,也使用continue可以直接跳入下一轮循环。
例如,下面的循环结构将输出1、3。
#![allow(unused)]
fn main() {
let mut x = 0;
loop {
x += 1;
if x == 5 {
break;
}
if x % 2 == 0 {
continue;
}
println!("{}", x);
}
}
loop也有默认返回值(),可以将其赋值给变量。例如,直接将上例的loop结构赋值给变量a:
#![allow(unused)]
fn main() {
let mut x = 0;
let a = loop {
...
};
println!("{:?}", a); // ()
}
作为一种特殊情况,当在loop中使用break时,break可以指定一个loop的返回值。
#![allow(unused)]
fn main() {
let mut x = 0;
let a = loop {
x += 1;
if x == 5 {
break x; // 返回跳出循环时的x,并赋值给变量a
}
if x % 2 == 0 {
continue;
}
println!("{}", x);
};
println!("var a: {:?}", a); // 输出 var a: 5
}
注意,只有loop中的break才能指定返回值,在while结构或for迭代结构中使用的break不具备该功能。
for迭代
Rust中的for只具备迭代功能。迭代是一种特殊的循环,每次从数据的集合中取出一个元素是一次迭代过程,直到取完所有元素,才终止迭代。
例如,Range类型是支持迭代的数据集合,Slice类型也是支持迭代的数据集合。
但和其他语言不一样,Rust数组不支持迭代,要迭代数组各元素,需将数组转换为Slice再进行迭代。
#![allow(unused)]
fn main() {
// 迭代Range类型:1..5
for i in 1..5 {
println!("{}", i);
}
let arr = [11, 22, 33, 44];
// arr是数组,&arr转换为Slice,Slice可迭代
for i in &arr {
println!("{}", i);
}
}
标签label
可以为loop结构、while结构、for结构指定标签,break和continue都可以指定标签来确定要跳出哪一个层次的循环结构。不仅如此,任何一个独立的{}语句块都可以加上标签,并使用break来提前退出标签。
例如:
#![allow(unused)]
fn main() {
// 'outer和'inner是标签名
'outer: loop {
'inner: while true {
break 'outer; // 跳出外层循环
}
}
}
需注意,loop结构中的break可以同时指定标签和返回值,语法为break 'label RETURN_VALUE。
例如:
#![allow(unused)]
fn main() {
let x = 'outer: loop {
'inner: while true {
break 'outer 3;
}
};
println!("{}", x); // 3
}
还可以为独立的{}语句块添加标签,并在某些条件下提前退出语句块,在有些场景下非常友好:
#![allow(unused)]
fn main() {
'hello: {
if COND {
CODE1...
break 'hello;
}
// 如果执行了上面的break,就不会执行到下面的代码
CODE2...
}
}