控制流

能够根据条件是否为true和 当 Condition 为true是基本的构建块 在大多数编程语言中。可让您控制的最常见结构 Rust 代码的执行流程是if表达式和循环。

if表达 式

ifexpression 允许您根据条件对代码进行分支。你 提供一个条件,然后声明“如果满足此条件,则运行此块 的代码。如果不满足条件,请不要运行此代码块。

projects 目录中创建一个名为 branches 的新项目以进行浏览 这if表达。在 src/main.rs 文件中,输入以下内容:

文件名: src/main.rs

fn main() {
    let number = 3;

    if number < 5 {
        println!("condition was true");
    } else {
        println!("condition was false");
    }
}

if表达式以关键字if,后跟一个 condition。在 在这种情况下,条件检查变量number具有 值小于 5。如果条件为true紧跟在大括号内的 condition 之后。代码块 与if表达式有时称为 arms, 就像match表达式中讨论的表达式 猜秘密数字“部分。

或者,我们还可以包含一个else表达式,我们选择执行 在这里,为了给程序提供一个替代的代码块来执行,如果 condition 评估为false.如果您未提供elseexpression 和 条件为false,程序将仅跳过if阻止并继续前进 到下一段代码。

尝试运行此代码;您应该会看到以下输出:

$ cargo run
   Compiling branches v0.1.0 (file:///projects/branches)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s
     Running `target/debug/branches`
condition was true

让我们尝试更改number设置为使条件false要查看会发生什么情况:

fn main() {
    let number = 7;

    if number < 5 {
        println!("condition was true");
    } else {
        println!("condition was false");
    }
}

再次运行该程序,并查看输出:

$ cargo run
   Compiling branches v0.1.0 (file:///projects/branches)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s
     Running `target/debug/branches`
condition was false

还值得注意的是,此代码中的 condition 必须是bool.如果 条件不是bool,我们将收到一个错误。例如,尝试运行 以下代码:

文件名: src/main.rs

fn main() {
    let number = 3;

    if number {
        println!("number was three");
    }
}

ifcondition 的计算结果为3这一次,Rust 抛出一个 错误:

$ cargo run
   Compiling branches v0.1.0 (file:///projects/branches)
error[E0308]: mismatched types
 --> src/main.rs:4:8
  |
4 |     if number {
  |        ^^^^^^ expected `bool`, found integer

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

该错误表明 Rust 期望bool但得到一个整数。与 Ruby 和 JavaScript 等语言,Rust 不会自动尝试 将非布尔类型转换为布尔类型。您必须明确并始终提供if,以 Boolean 作为其条件。如果我们想使用if要运行的代码块 仅当数字不等于0,例如,我们可以更改ifexpression 设置为以下内容:

文件名: src/main.rs

fn main() {
    let number = 3;

    if number != 0 {
        println!("number was something other than zero");
    }
}

运行此代码将打印number was something other than zero.

处理多个条件else if

您可以通过组合使用多个条件ifelseelse if表达。例如:

文件名: src/main.rs

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");
    }
}

该程序有四种可能的路径。运行后,您应该 请参阅以下输出:

$ cargo run
   Compiling branches v0.1.0 (file:///projects/branches)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s
     Running `target/debug/branches`
number is divisible by 3

当此程序执行时,它会检查每个if表达式并执行 条件计算结果为true.请注意,即使 虽然 6 可以被 2 整除,但我们看不到输出number is divisible by 2, 我们也看不到number is not divisible by 4, 3, or 2文本else块。这是因为 Rust 只执行第一个区块truecondition,一旦找到一个,它甚至不会检查其余的。

使用太多else if表达式可能会使您的代码变得混乱,因此,如果您有更多 而不是 1,则可能需要重构代码。第 6 章描述了一个强大的 名为match对于这些情况。

iflet陈述

因为if是一个表达式,我们可以在let语句将结果分配给一个变量,如示例 3-2 所示。

文件名: src/main.rs
fn main() {
    let condition = true;
    let number = if condition { 5 } else { 6 };

    println!("The value of number is: {number}");
}
示例 3-2:分配ifexpression 添加到变量

number变量将绑定到一个基于if表达。运行以下代码以查看会发生什么:

$ cargo run
   Compiling branches v0.1.0 (file:///projects/branches)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.30s
     Running `target/debug/branches`
The value of number is: 5

请记住,代码块的计算结果为其中的最后一个表达式,并且 数字本身也是表达式。在这种情况下, 整个ifexpression 取决于执行的代码块。这意味着 值,这些值有可能成为if必须是 相同类型;在示例 3-2 中,两个ifarm 和else手臂i32整数。如果类型不匹配,如下所示 example,我们将得到一个错误:

文件名: src/main.rs

fn main() {
    let condition = true;

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

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

当我们尝试编译此代码时,我们将收到一个错误。这ifelse武器 具有不兼容的值类型,并且 Rust 会准确指示 在程序中找到问题:

$ cargo run
   Compiling branches v0.1.0 (file:///projects/branches)
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 `branches` (bin "branches") due to 1 previous error

if块的计算结果为整数,而 这elseblock 的计算结果为 string。这不起作用,因为变量必须 有一个类型,并且 Rust 需要在编译时知道number变量是。了解number让 compiler 验证类型在我们使用的任何地方都有效number.Rust 不会 如果number仅在运行时确定;这 编译器会更复杂,并且对代码的保证更少 如果它必须跟踪任何变量的多个假设类型。

使用 Loops 重复

多次执行一个代码块通常很有用。对于此任务, Rust 提供了几个循环,这些循环将遍历循环内的代码 body 到最后,然后立即从头开始。试验 使用 Loops,让我们创建一个名为 Loops 的新项目。

Rust 有三种循环:loop,whilefor.让我们逐一尝试。

重复代码loop

loopkeyword 告诉 Rust 一遍又一遍地执行一段代码 永远或直到您明确告诉它停止。

例如,将 loops 目录中的 src/main.rs 文件更改为 喜欢这个:

文件名: src/main.rs

fn main() {
    loop {
        println!("again!");
    }
}

当我们运行这个程序时,我们会看到again!一遍又一遍地连续打印 直到我们手动停止程序。大多数终端都支持键盘快捷键 - 中断卡在连续 圈。试一试:ctrlc

$ cargo run
   Compiling loops v0.1.0 (file:///projects/loops)
    Finished dev [unoptimized + debuginfo] target(s) in 0.29s
     Running `target/debug/loops`
again!
again!
again!
again!
^Cagain!

符号^C表示您按 - 的位置。你 可能会也可能看不到单词ctrlcagain!打印在^C,具体取决于 代码在接收到中断信号时处于循环中。

幸运的是,Rust 还提供了一种使用代码跳出循环的方法。你 可以放置break关键字来告诉程序何时停止 执行循环。回想一下,我们在第 2 章的“正确猜测后退出”部分的猜谜游戏中这样做了,当用户赢得游戏时退出程序 猜对数字。

我们还使用了continue在猜谜游戏中,它在循环中告诉程序 跳过此循环迭代中的任何剩余代码,并转到 next 迭代。

从循环返回值

的用途之一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并将其初始化为0.然后我们声明一个名为result保存从 循环。在循环的每次迭代中,我们都会添加1counter变量 然后检查counter等于10.当它出现时,我们使用breakkeyword 的值为counter * 2.在循环之后,我们使用 分号结束将值分配给result.最后,我们 打印result,在本例中为20.

您还可以return从循环内部。而break仅退出当前 圈return始终退出当前函数。

Loop 标签以消除多个 Loop 之间的歧义

如果 Loop 中有 Loops,breakcontinue应用于最里面 循环。您可以选择在循环上指定一个循环标签,该标签 然后,您可以将其与breakcontinue以指定这些关键字 应用于带标签的 Loop,而不是最内层的 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}");
}

外循环的标签'counting_up,它将从 0 到 2 递增。 没有标签的内循环从 10 到 9 倒计时。第一个break那 未指定 Label 将仅退出内部循环。这break 'counting_up;语句将退出外部循环。此代码打印:

$ cargo run
   Compiling loops v0.1.0 (file:///projects/loops)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.58s
     Running `target/debug/loops`
count = 0
remaining = 10
remaining = 9
count = 1
remaining = 10
remaining = 9
count = 2
remaining = 10
End count = 2

条件循环while

程序通常需要评估循环中的条件。虽然 condition 为true,则循环运行。当条件不再true这 程序调用break,停止循环。可以实现行为 就像这样使用loop,if,elsebreak;你可以 如果您愿意,现在就在一个程序中尝试一下。然而,这种模式非常普遍 Rust 有一个内置的语言结构,称为while圈。在 示例 3-3,我们使用while循环程序 3 次,每次循环倒计时 time,然后在循环后打印一条消息并退出。

文件名: src/main.rs
fn main() {
    let mut number = 3;

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

        number -= 1;
    }

    println!("LIFTOFF!!!");
}
示例 3-3:使用while循环,以便在条件为 true 时运行代码

此构造消除了大量嵌套,如果您使用loop,if,elsebreak,而且情况更清晰。虽然条件 计算结果为true,则代码运行;否则,它将退出循环。

循环遍历集合for

您还可以使用whileconstruct 遍历 集合,例如数组。例如,示例 3-4 中的循环打印了每个 元素a.

文件名: src/main.rs
fn main() {
    let a = [10, 20, 30, 40, 50];
    let mut index = 0;

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

        index += 1;
    }
}
示例 3-4:使用while

在这里,代码通过数组中的元素进行计数。它从索引开始0,然后循环,直到到达数组中的最终索引(即 什么时候index < 5不再true).运行此代码将打印每个 元素中:

$ cargo run
   Compiling loops v0.1.0 (file:///projects/loops)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.32s
     Running `target/debug/loops`
the value is: 10
the value is: 20
the value is: 30
the value is: 40
the value is: 50

正如预期的那样,所有 5 个 array 值都显示在终端中。即使index将达到5在某些时候,循环在尝试之前停止执行 从数组中获取第六个值。

但是,这种方法容易出错;如果出现以下情况,我们可能导致程序 panic 索引值或测试条件不正确。例如,如果您更改了 的定义a数组设置为具有四个元素,但忘记更新 condition 设置为while index < 4,代码将 panic。它也很慢,因为 编译器添加运行时代码以执行条件检查是否 index 在循环的每次迭代中都在数组的边界内。

作为更简洁的替代方法,您可以使用for循环并执行一些代码 对于集合中的每个项目。一个forloop 类似于示例 3-5 中的代码。

文件名: src/main.rs
fn main() {
    let a = [10, 20, 30, 40, 50];

    for element in a {
        println!("the value is: {element}");
    }
}
示例 3-5:使用for

当我们运行这段代码时,我们将看到与示例 3-4 中相同的输出。更多 重要的是,我们现在提高了代码的安全性,并消除了 因超出数组末尾或不超出数组末尾而可能导致的 bug 的几率 走得足够远,缺少一些项目。

使用for循环中,如果 您更改了数组中值的数量,就像使用 Method 在示例 3-4 中使用。

安全性和简洁性forLoop 使它们成为最常用的 Loop 构造。即使在您想要运行某些代码 a 一定次数,例如在使用while圈 在示例 3-3 中,大多数 Rustacean 会使用for圈。实现目标的方法 将使用Range,由标准库提供,该库生成 所有数字按顺序从一个数字开始,在另一个数字之前结束 数。

以下是使用forloop 和另一种方法 我们还没有讨论过,rev,要反转范围:

文件名: src/main.rs

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

这段代码好一点,不是吗?

总结

你成功了!这是一个相当大的章节:您了解了变量、标量 和复合数据类型、函数、注释、if表达式和循环!自 练习本章中讨论的概念,尝试构建程序以 执行以下作:

  • 在华氏度和摄氏度之间转换温度。
  • 生成第 n个斐波那契数。
  • 打印圣诞颂歌“The Twelve Days of Christmas”的歌词。 利用歌曲中的重复。

当你准备好继续时,我们将讨论 Rust 中一个在其他编程语言中通常不存在的概念:所有权。

本文档由官方文档翻译而来,如有差异请以官方英文文档(https://doc.rust-lang.org/)为准