文章目录
- Rust语言入门第七篇-控制流
- If 表达式
- 基本结构
- 特点和规则
- 示例
- let 语句中使用 if
- loop 循环
- 基本结构
- 特点
- 示例
- 综合示例
- while 循环
- 基本结构
- 特点
- 示例
- 综合示例
- 与 `loop` 循环的区别
- for 循环
- 基本结构
- 详细说明
- 特点
- 示例
- 综合示例
- match 表达式
- `match`表达式的语法结构
- 示例代码
Rust语言入门第七篇-控制流
Rust 的控制流是指程序执行过程中根据不同的条件选择不同的代码路径的能力。这是编程中一个核心的概念,帮助开发者实现逻辑判断、循环执行等基本结构。Rust 语言提供了一些关键的控制流构造,主要包括 if 表达式、match 表达式、loop、while、for 循环等。
If 表达式
if表达式在很多语言中很常见!if表达式允许条件不同执行不同的代码。
Rust 语言中的 if
表达式用于基于条件来执行不同的代码块。它的结构相对直接,但有几个特点需要注意,这些特点体现了 Rust 作为一门类型安全和表达式导向语言的特性。以下是 if
表达式的基本结构和一些关键点:
基本结构
if 条件 {
// 当条件为 true 时执行的代码块
} else {
// 当条件为 false 时执行的代码块(可选)
}
特点和规则
-
条件必须是布尔类型 (
bool
):与某些其他语言不同,Rust 不会隐式地将非布尔类型的值转换为布尔值。你需要确保if
后面的条件表达式直接评估为一个布尔值。 -
代码块 (
{}
):即使if
或else
分支只包含一条语句,你也必须使用花括号{}
将其包裹起来。这是为了保持一致性,并且因为 Rust 中几乎所有东西都是表达式。 -
可选的
else if
:为了实现多路分支,你可以链式使用else if
语句。
if 条件1 {
// 当条件1为 true 时执行
} else if 条件2 {
// 当条件1为 false 并且条件2为 true 时执行
} else {
// 当以上所有条件都为 false 时执行
}
- 表达式 vs. 语句:在 Rust 中,
if
表达式实际上是一个表达式,这意味着它有返回值。每个分支的最后一项(即最后一个表达式的值)决定了整个if
表达式的值。这使得if
表达式可以在需要值的上下文中使用,比如赋值给变量或作为函数的返回值。
示例
fn calculate_result(score: i32) -> &'static str {
if score > 90 {
"Excellent"
} else if score > 75 {
"Good"
} else {
"Keep trying"
}
}
fn main() {
let score = 85;
let result = calculate_result(score);
println!("{}", result); // 输出 "Good"
}
在这个示例中,calculate_result
函数根据分数返回不同的评价等级,展示了 if
表达式如何用作返回值的计算。
let 语句中使用 if
在 Rust 中,结合 let 和 if 一起使用的结构被称为 if let 表达式。这是一种简化版的 match 表达式,专门用于处理单一的模式匹配情况
fn main() {
let condition = true;
let number = if condition { 5} else { 6 };
println!("数字的值为:{number}");
}
这段 Rust 代码演示了如何使用 if
表达式作为值赋给变量,体现了 Rust 中 if
表达式不仅用于控制流,还可以直接产生值的特性。下面是详细的解释:
-
初始化变量
condition
: 首先,定义了一个布尔型变量condition
并将其值设为true
。let condition = true;
-
使用
if
表达式赋值给number
: 接下来,定义了一个变量number
,其值由一个if
表达式决定。这个if
表达式检查condition
的值:- 如果
condition
为true
,则执行{ 5 }
代码块,表达式的值为5
。 - 如果
condition
为false
,则执行{ 6 }
代码块,表达式的值为6
。
这里,
if
表达式直接充当了一个值生成器,其结果(5
或6
)被赋给了number
。let number = if condition { 5 } else { 6 };
- 如果
loop 循环
loop
是 Rust 中的一种循环结构,它没有内置的终止条件,因此会一直执行循环体内的代码,直到遇到 break
语句为止。这对于需要无限循环或循环次数未知的情况非常有用。
基本结构
loop {
// 循环体内的代码
// 使用 break 语句来退出循环
}
特点
- 无终止条件:
loop
没有内置的终止条件,这意味着它会一直运行,直到你显式地使用break
语句来停止它。 - 使用
break
退出:要终止loop
循环,你必须在循环体内使用break
语句。 - 使用
continue
跳过迭代:你也可以使用continue
语句来跳过当前迭代,并立即开始下一次迭代。
示例
下面是一个简单的 loop
循环示例,该示例将会数到 10 并打印出每个数字。当计数达到 10 时,使用 break
语句退出循环。
fn main() {
let mut count = 0;
loop {
println!("Count: {}", count);
count += 1;
if count == 10 {
break;
}
}
}
综合示例
下面是一个稍微复杂的例子,该示例使用 loop
来模拟一个简单的游戏,玩家可以选择继续游戏或退出。
use std::io;
fn main() {
let mut choice = 'y';
loop {
println!("Welcome to the game!");
println!("Do you want to play? (y/n)");
let mut input = String::new();
io::stdin().read_line(&mut input)
.expect("Failed to read line");
choice = input.trim().chars().next().unwrap_or('n');
if choice == 'y' {
println!("Playing...");
} else {
println!("Exiting...");
break;
}
}
}
在这个示例中:
- 我们定义了一个变量
choice
来存储用户的输入。 - 使用
loop
来重复询问用户是否想继续玩游戏。 - 使用
std::io::stdin().read_line()
来读取用户的输入。 - 使用
break
语句来结束循环,当用户输入 ‘n’ 时。
这个示例展示了如何使用 loop
来构建一个无限循环,并通过用户输入来控制循环的终止。
while 循环
基本结构
while condition {
// 当条件为 true 时执行的代码块
}
特点
while
循环会在每次迭代前检查给定的条件。- 如果条件为
true
,则执行循环体内的代码。 - 如果条件为
false
,则不执行循环体内的代码,并且循环终止。 - 与
loop
相比,while
循环提供了内置的终止条件,这使得它适合于那些你知道循环应该执行多少次或何时应停止的情况。 - 在循环体内部,你可以使用
break
语句来提前终止循环。 - 你也可以使用
continue
语句来跳过当前迭代并开始下一次迭代。
示例
下面是一个简单的 while
循环示例,该示例将会数到 10 并打印出每个数字。
fn main() {
let mut count = 0;
while count < 10 {
println!("Count: {}", count);
count += 1;
}
}
综合示例
下面是一个稍微复杂的例子,该示例使用 while
循环来模拟一个简单的游戏,玩家可以选择继续游戏或退出。
use std::io;
fn main() {
let mut choice = 'y';
while choice == 'y' {
println!("Welcome to the game!");
println!("Do you want to play? (y/n)");
let mut input = String::new();
io::stdin().read_line(&mut input)
.expect("Failed to read line");
choice = input.trim().chars().next().unwrap_or('n');
if choice == 'y' {
println!("Playing...");
} else {
println!("Exiting...");
}
}
}
在这个示例中:
- 我们定义了一个变量
choice
来存储用户的输入。 - 使用
while
来重复询问用户是否想继续玩游戏,只要choice
为'y'
。 - 使用
std::io::stdin().read_line()
来读取用户的输入。 - 用户可以通过输入
'n'
来退出游戏,此时choice
变为'n'
,while
循环的条件变为false
,循环终止。
与 loop
循环的区别
while
循环在每次迭代之前检查条件是否为true
,而loop
没有内置的终止条件。while
循环适合于那些你知道循环应该执行多少次或何时应停止的情况,而loop
更适用于不知道循环次数的情况。- 在
while
循环中,如果初始条件就是false
,那么循环体内的代码将不会被执行;而在loop
循环中,你必须显式地使用break
语句来终止循环。
for 循环
基本结构
for variable in iterable {
// 循环体内的代码
}
详细说明
for
关键字:开始for
循环。variable
:循环变量,用于保存每次迭代中的当前元素。这可以是任意有效的 Rust 标识符。in
关键字:表示循环变量将在接下来的迭代中遍历的集合或范围。iterable
:可以被迭代的对象,它可以是实现了IntoIterator
特性的任何类型,例如向量 (Vec<T>
), 数组 ([T; N]
), 字符串 (String
或&str
), 范围 (a..b
或a..=b
) 等。{ ... }
:花括号包围的代码块,称为循环体。这里包含了每次迭代要执行的代码。
特点
- 自动迭代:
for
循环会自动迭代iterable
中的每个元素,并在每次迭代时将当前元素绑定到variable
。 - 类型推断:编译器会自动推断
variable
的类型,使其与iterable
中的元素类型相匹配。 - 范围迭代:可以使用范围来指定一个数值序列,例如
0..10
代表从 0 到 9 的整数序列。 - 引用迭代:默认情况下,
for
循环迭代的是iterable
中元素的引用,这意味着原始数据结构不会被修改。如果你想获得元素的所有权,可以使用.into_iter()
方法。 - 可变迭代:如果你想在迭代过程中修改元素,可以使用
iter_mut()
方法来获取可变引用。
示例
下面是一个简单的 for
循环示例,该示例将会遍历一个向量并打印出每个元素。
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
for number in &numbers { // 迭代向量中的元素引用
println!("Number: {}", number);
}
for number in numbers.into_iter() { // 迭代向量中的元素所有权
println!("Number: {}", number);
}
// 使用范围迭代
for i in 0..10 {
println!("i: {}", i);
}
}
综合示例
下面是一个稍微复杂的例子,该示例使用 for
循环来遍历一个字符串,并统计其中每个字符出现的次数。
use std::collections::HashMap;
fn main() {
let text = "hello world";
let mut char_counts = HashMap::new();
for c in text.chars() { // 遍历字符串中的字符
let count = char_counts.entry(c).or_insert(0);
*count += 1;
}
for (char, count) in &char_counts { // 遍历 HashMap 中的键值对
println!("'{}': {}", char, count);
}
}
在这个示例中:
- 我们定义了一个字符串
text
。 - 使用
for
循环来遍历字符串中的每个字符。 - 使用
HashMap
来记录每个字符出现的次数。 - 使用
entry
方法来获取或创建一个条目,然后增加计数。 - 最后再次使用
for
循环来遍历HashMap
并打印每个字符及其对应的计数。
match 表达式
Rust语言中的match
表达式是模式匹配的一种方式,用于在多个分支中选择一个执行。它类似于其他编程语言中的switch
语句,但是功能更加强大和灵活,可以匹配复杂的模式而不仅仅是整数或字符串值。
match
表达式的语法结构
基本的match
表达式的语法如下:
match value {
pattern if guard_condition => expression,
// 更多的 pattern...
_ => default_expression, // 可选的默认分支
}
value
是你要匹配的值。pattern
是你想要匹配的模式。if guard_condition
是一个可选的条件,只有当这个条件为真时,模式才匹配。expression
是如果模式匹配成功后要执行的代码块。_
是一个通配符模式,表示如果没有其他模式匹配成功,则执行default_expression
。
注意:所有可能的情况都必须被覆盖,要么显式地处理,要么通过使用通配符_
来提供一个默认分支。如果不能覆盖所有情况,编译器将会报错。
示例代码
// 定义一个枚举类型UsState,这里只列举了两个州作为示例
enum UsState {
Alabama,
Alaska,
}
// 定义一个函数say_hello,接收一个UsState类型的参数state
fn say_hello(state: UsState) {
// 使用match表达式来匹配传入的state值
match state {
// 如果state是Alabama,则打印"Let's go to the beach!"
UsState::Alabama => println!("Let's go to the beach!"),
// 如果state是Alaska,则打印"Maybe visit the glaciers?"
UsState::Alaska => println!("Maybe visit the glaciers?"),
// 如果没有其他模式匹配成功(即state不是Alabama也不是Alaska),则打印"Some other state."
_ => println!("Some other state."),
}
}
// 主函数,程序的入口点
fn main() {
// 创建一个UsState枚举实例
let current_state = UsState::Alabama;
// 调用say_hello函数并传入当前的州
say_hello(current_state);
}
// 定义一个名为calculate_length的函数,接收一个包含两个i32值的元组作为参数
fn calculate_length(x: (i32, i32)) -> String {
// 使用match表达式来匹配元组x中的元素
match x {
// 如果元组的第一个元素和第二个元素都是0,则返回"Origin"
(0, 0) => String::from("Origin"), // 这里使用String::from方法创建一个新的字符串
// 如果元组的第一个元素是0但第二个元素不是0,则格式化并返回一个字符串
(0, y) => format!("On the Y axis at {}", y), // format!宏用于创建格式化的字符串
// 如果元组的第二个元素是0但第一个元素不是0,则格式化并返回一个字符串
(x, 0) => format!("On the X axis at {}", x), // format!宏用于创建格式化的字符串
// 如果元组的两个元素都不是0,则格式化并返回一个包含两个坐标值的字符串
(x, y) => format!("({}, {})", x, y), // format!宏用于创建格式化的字符串
}
}
// 主函数,程序的入口点
fn main() {
// 创建一个包含两个i32值的元组
let coordinates = (3, 4);
// 调用calculate_length函数并传入坐标值
let length = calculate_length(coordinates);
// 打印返回的字符串
println!("{}", length); // 输出"(3, 4)"
}