Rust 中的条件、循环结构——Rust语言基础08

1. 前言

到目前为止,我们所完成的代码都是按照编写顺序依次的执行下去,这样的代码结构称为顺序结构。顺序结构的代码逻辑较为简单,但相对的能够完成的工作也相对较少,由于我们遇到的大多数现实问题都会是条件判断问题。

比如:
1、如果明天下雨,那我就吃火锅,否则就粗去玩。
2、如果吃火锅,那我得涮牛肚,否则就等下次再吃。
3、如果粗去玩,那一定得带上我那超级可爱的女 (男) 朋友,否则就和她(他)吃火锅。
4、如果有真的有的话,那早都去了,否则也不会在这写 Blog 了,🤦‍♂️!

所以,看到了吧!根据条件是否为真时执行某些代码或者在条件为真是重复执行某些代码的能力,是大多数编程语言的基本构建块。这样的条件分支结构以及循环结构也被称为控制流。

2. if 表达式(分支结构)

让我们先面见最最最常见的结构 if 表达式。它将会根据条件为程序提供多条分支代码。开发者需要提供给 if 表达式一个条件,然后声明:“如果满足此条件,执行此块代码。否则,不要执行此块代码。”

与之前一样,我们先创建一个新的项目:

imaginemiracle:rust_projects$ cargo new branches
     Created binary (application) `branches` package
imaginemiracle:rust_projects$ cd branches/
imaginemiracle:branches$ ls
Cargo.toml  src

src/main.rs 源文件的内容替换为下面代码:

fn main() {
    let relative_air_humidity = 60;

    if relative_air_humidity > 70 {	
        println!("It will rain tomorrow.");
    } else {
        println!("It won't rain tomorrow.");
    }
}

[注]:当空气湿度大于 70% 则表示将会下雨。

所有的 if 表达式都以 if 关键字开头,后面紧跟的便是判断条件。当前情况的 if 表达式会判断 relative_air_humidity 的值是否大于 70。如果条件为真则会执行紧跟在大括号内的代码。与 if 表达式的条件相关的代码块也称为 arm,之前在编写猜字谜游戏时也遇到过,match 表达式,其中的条件相关的代码也被称为 arm

对于 if 表达式,我们还可以为其添加一个 else 表达式(可选),当 if 表达式条件为假时,由 else 表达式为程序提供另一条分支。若没有 else 表达式,且 if 表达式的条件为假时,程序将会跳过 if 表达式的分支块,继续执行下一段代码。

执行上述代码:

imaginemiracle:branches$ cargo run
   Compiling branches v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/branches)
    Finished dev [unoptimized + debuginfo] target(s) in 0.92s
     Running `target/debug/branches`
It won't rain tomorrow.

可以自己将 relative_air_humidity 变量的值修改为大于 70,再尝试运行。或者修改代码逻辑,将判断条件修改为小于 70,或者不等于 70。这里便不检验了。(常见的判断 ><!===

需要注意的是,在本例中的条件必须是 bool 类型,若条件不是 bool 类型,编译器将会报错。尝试修改检验:

fn main() {
    let relative_air_humidity = 60;

    if relative_air_humidity {
        println!("It will rain tomorrow.");
    } else {
        println!("It won't rain tomorrow.");
    }
}

运行查看:

imaginemiracle:branches$ cargo run
   Compiling branches v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/branches)
error[E0308]: mismatched types
 --> src/main.rs:4:8
  |
4 |     if relative_air_humidity {
  |        ^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found integer

For more information about this error, try `rustc --explain E0308`.
error: could not compile `branches` due to previous error

可以看到编译器报错告诉我们说,if 表达式这块预期得到的是 bool 类型的值,但现在得到的确实一个整数。这一点 RustCRuby 以及 JavaScript 等语言不同,Rust 不会自动的将这里判断的条件转换为 bool 类型。(在 CRuby 以及 JavaScript 中可以这样)因此开发者需要始终为 if 表达式提供明确的 bool 值作为其条件。

2.1. else if 处理多个条件

我们可以通过在 if 表达式与 else 表达式组合来实现对多个条件的判断。

示例:

fn main() {
    let number = 6;

    if number % 4 == 0 {
        println!("number is divisible by 4");
    } else if number % 3 == 0 {
        println!("number is divisible by 3");
    } else if number % 2 == 0 {
        println!("number is divisible by 2");
    } else {
        println!("number is not divisible by 4, 3, or 2");
    }
}

该程序会有四种不同可能的分支。执行程序:

imaginemiracle:branches$ cargo run
   Compiling branches v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/branches)
    Finished dev [unoptimized + debuginfo] target(s) in 0.56s
     Running `target/debug/branches`
number is divisible by 3

当指定该段条件分支语句时,Rust 会依次检检查每个 if表达式,并执行第一个条件成立的分支。这里可以看到程序判断 6 可以被 3 整除,则输出了 number is divisible by 3,之后便跳出整个条件分支。

需要注意的是,即便 6 也可以被 2 整除,但在这里仍不会输出 number is divisible by 2,也不会执行到 else 分支的代码块。这是因为 Rust 只会执行第一个条件为真的分支,一旦找到一个条件为真的分支,则不会对其他未检查的条件做任何判断。

但是请注意,如果使用过多的 else if 表达式,将会使构建的代码变得混乱不堪,如果存在多个表达式,则需要对每个分支修改,甚至重构代码。因此更加建议使用 match 表达式。(会在后面的文章详细展开)

2.2. 在 let 语句中使用 if 表达式

由于 if 是一个表达式,因此可以将其应用于语句中,如在 let 语句中使用 if 表达式来为其分配一个值。

示例:

fn main() {
    let condition = true;
    let number = if condition { 5 } else { 6 };

    println!("The value of number is: {number}");
}

这段代码将会为 number 赋值为 5。由 {} 括起来的代码块返回的值或计算的结果取决于其中最后一个表达式,在此代码中 56 就是各自最后的表达式,因此两块代码块的结果正如表达式所述一样。

需要注意的是为 let 语句赋值时,则需要 ifelse 表达式各自的 arm 产生的结果类型保持一致,若类型不同则会报错。
示例:

fn main() {
    let condition = true;

    let number = if condition { 5 } else { "six" };

    println!("The value of number is: {number}");
}

执行代码:

imaginemiracle:control_flow$ cargo run
   Compiling control_flow v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/control_flow)
error[E0308]: `if` and `else` have incompatible types
 --> src/main.rs:4:44
  |
4 |     let number = if condition { 5 } else { "six" };
  |                                 -          ^^^^^ expected integer, found `&str`
  |                                 |
  |                                 expected because of this

For more information about this error, try `rustc --explain E0308`.
error: could not compile `control_flow` due to previous error

if 块中的表达式计算结果为整数,else 块中的表达式计算结果为字符串。这是行不通的,因为变量必须具有单一类型,并且 Rust 需要在编译时明确地知道 number 变量是什么类型。知道 number 的类型可以让编译器在我们使用 number 的任何地方验证该类型是否有效。如果仅在运行时确定 number 类型,Rust 将无法做到这一点;如果编译器必须跟踪所有变量的多个假设类型,编译器会更复杂,并且对代码的保证更少。

但像这样两个块的结果类型一致则不会有任何问题。

fn main() {
    let condition = true;

    let number = if condition { "five" } else { "six" };

    println!("The value of number is: {number}");
}

3. 循环结构

让一段代码重复的多次执行,这样的功能是很有必要的。Rust 中有三种循环:loopwhile 以及 for

3.1. loop 重复循环

loop 关键则告诉 Rust 永远的重复执行一段代码,直到你明确的告诉需要停止为止,否则将一直重复执行下去(直到你的机器没电为止)。

示例:
新建一个 loops 的项目来做实验。

cargo new loops
fn main() {
    loop {
        println!("loop!");
    }
}

执行该代码,不出意外的话将不会停止的打印 loop,我们必须使用 Ctrl + c 快捷键来终止程序。

   Compiling loops v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/loops)
    Finished dev [unoptimized + debuginfo] target(s) in 0.64s
     Running `target/debug/loops`
loop
loop
loop
loop
loop
loop
loop
loop
loop
loop
loop
loop
loop
loop^C

该符号 ^C 代表按下 Ctrl+c 的位置。可能会或可能不会看到在 loop 之后打印的字符 ^C,具体取决于代码在收到中断信号时在循环中的位置。

我们可以在循环中使用 break 关键字来退出循环。

也可以使用 continue 关键字来提前结束本次循环,直接进入下一次循环。

[注]:这两个关键字在之前的猜谜游戏中都使用过,本文不再赘述。

3.1.1. 利用循环提供返回值

从循环中获得返回值,我们可以利用这一点来获取循环退出的原因,从而可以分析循环操作是否完成或成功,当然还有其它的用途。

来看看要如何获取吧:
示例:

fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;

        if counter == 10 {
            break counter * 2;
        }
    };

    println!("The result is {result}");
}

在进入 loop 循环之前,首先声明了一个名为 counter 的变量并初始化为 0。然后再声明了一个名为 result 的变量,用来保存循环返回的值。在每次的循环迭代中,counter 变量自加 1,当 counter10if 表达式判断条件为真,进入代码块中执行 break counter * 2; 语句,使用 break 关键后跟具体值的方式来为循环返回值。在整个 loop 循环体后添加 ; 表示结束了 let 语句。

3.1.2. 利用循环标签消除多个循环之间的歧义

若循环中嵌套循环,那么在最内层循环中使用 breakcontinue 。当为循环指定了一个循环标签后,那么则可以使用 breakcontinue 指定作用于哪个(层) loop 循环。循环标签的开头必须以单引号开头。

示例:

fn main() {
    let mut count = 0;
    'counting_up: loop {
        println!("count = {count}");
        let mut remaining = 10;

        loop {
            println!("remaining = {remaining}");
            if remaining == 9 {
                break;
            }
            if count == 2 {
                break 'counting_up;
            }
            remaining -= 1;
        }

        count += 1;
    }
    println!("End count = {count}");
}

执行代码:

imaginemiracle:loops$ cargo run
   Compiling loops v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/loops)
    Finished dev [unoptimized + debuginfo] target(s) in 0.44s
     Running `target/debug/loops`
count = 0
remaining = 10
remaining = 9
count = 1
remaining = 10
remaining = 9
count = 2
remaining = 10
End count = 2

外部循环有标签 'counting_up,它会从 0 计数到 2。没有标签的内部循环从 10 倒数到 9。第一个 break 没有指定标签的将只退出内部循环。该 break 'counting_up; 语句将退出外循环。

3.2. while 循环

while 循环会在每次循环前判断当前条件是否为真,若为真则进去循环体,为假则退出 while 循环。

这样的逻辑也可以通过在 loop 循环中使用 ifelse 以及 break 的组合来实现,每次都这样代码的编写不够便捷其的结构显得不够清晰,因此 Rust 将其内置为一种循环结构称为 while 循环。

示例:

fn main() {
    let mut number = 3;

    while number != 0 {
        println!("{number}!");

        number -= 1;
    }

    println!("LIFTOFF!!!");
}

此示例代码将会循环 3 次,完成三次打印后退出循环。

我们可以使用 while 来构造一个集合的遍历。
示例:

fn main() {
    let a = [10, 20, 30, 40, 50];
    let mut index = 0;

    while index < 5 {
        println!("the value is: {}", a[index]);

        index += 1;
    }
}

代码中首先定义了一个名为 a 整型数组,再定义了一个名为 index 的变量作为数组的索引。当 while 判断条件索引值超过数组最大元素时将退出。
运行此代码:

imaginemiracle:loops$ cargo run
   Compiling loops v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/loops)
    Finished dev [unoptimized + debuginfo] target(s) in 0.74s
     Running `target/debug/loops`
the value is: 10
the value is: 20
the value is: 30
the value is: 40
the value is: 50

正如预期的那样,所有五个数组值都出现在终端中。即使 index 会在某个点达到 5,循环也会在尝试从数组中获取第六个值之前停止执行。

3.3. for 循环

如上文中使用 while 循环实现的数组遍历,但这样的方法很容易会出错。如果给的条件中索引值不正确,那么将会导致程序崩溃。正如上面代码,倘若将其中数组元素删除一个,但是忘记更新判断条件为 while index < 4 ,此时你的代码将会报错。

那么作为更加便捷的方案,我们可以使用 for 循环处理像这样的条件。
示例:

fn main() {
    let a = [10, 20, 30, 40, 50];

    for element in a {
        println!("the value is: {element}");
    }
}

运行此程序将会得到与之前同样的输出,但是这样消除了代码出错的可能,提高了代码的安全性。

我们经常会使用 for 完成指定次数的循环,Rust 同样可以。
示例:

fn main() {
    for number in 1..4 {
        println!("{number}!");
    }
    println!("LIFTOFF!!!");
}

运行此段代码:

imaginemiracle:loops$ cargo run
   Compiling loops v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/loops)
    Finished dev [unoptimized + debuginfo] target(s) in 0.53s
     Running `target/debug/loops`
1!
2!
3!
LIFTOFF!!!

此段 for 循环条件相当于 C/C++ 语言中的 for (int i = 1; i < 4; i++)。这是一段顺序的计数,当我们可以修改为逆序的计数。
示例:

fn main() {
    for number in (1..4).rev() {
        println!("{number}!");
    }
    println!("LIFTOFF!!!");
}

运行此代码:

imaginemiracle:loops$ cargo run
   Compiling loops v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/loops)
    Finished dev [unoptimized + debuginfo] target(s) in 0.57s                                                                                             		
    Running `target/debug/loops`
3!
2!
1!
LIFTOFF!!!

rev 是用来反转范围的方法。

#Review

到这里我们已经学会了变量、标量、复合数据类型、函数、注释以及 if 表达式和循环结构。すごいですね
接下来我们将会以两个练习的方式回顾之前学习的内容!!


Boys and Girls!!!
准备好了吗?下一节我们要开始做个小练习了哦!

不!我还没准备好,让我先回顾一下之前的。
上一篇《Rust 中的函数——Rust语言基础07》

我准备好了,掛かって来い(放马过来)!
下一篇《Rust 实现斐波那契数列——Rust语言基础09》


觉得这篇文章对你有帮助的话,就留下一个赞吧v*
请尊重作者,转载还请注明出处!感谢配合~
[作者]: Imagine Miracle
[版权]: 本作品采用知识共享署名-非商业性-相同方式共享 4.0 国际许可协议进行许可。
[本文链接]: https://blog.csdn.net/qq_36393978/article/details/125725722

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Imagine Miracle

爱你哟 =^ v ^=

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值