title: Rust-05-控制流
date: 2024/04/10
tags:

  • Rust
    categories:
  • 笔记

控制流

根据条件是否为真来决定是否执行某些代码,或根据条件是否为真来重复运行一段代码,是大部分编程语言的基本组成部分。Rust 代码中最常见的用来控制执行流的结构是 if 表达式和循环。

if 表达式

每个分支的代码块都必须以{}包裹,哪怕只有一行或一条表达式。

条件(谓词)表达式不必(不是不可以)使用()包裹。

条件(谓词)表达式必须是布尔值,Rust不会进行非布尔到布尔的自动类型转换。

在 let 语句中使用 if

因为 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:将 if 表达式的返回值赋给一个变量

number 变量将会绑定到表示 if 表达式结果的值上。运行这段代码看看会出现什么:

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

记住,代码块的值是其最后一个表达式的值,而数字本身就是一个表达式。在这个例子中,整个 if 表达式的值取决于哪个代码块被执行。这意味着 if 的每个分支的可能的返回值都必须是相同类型。

使用循环重复执行

多次执行同一段代码是很常用的,Rust 为此提供了多种循环loop),它们遍历执行循环体中的代码直到结尾并紧接着回到开头继续执行。

Rust 有三种循环:loopwhilefor

使用 loop 重复执行代码

loop 关键字告诉 Rust 一遍又一遍地执行一段代码直到你明确要求停止。

无限循环:

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

如果存在嵌套循环,breakcontinue 应用于此时最内层的循环。你可以选择在一个循环上指定一个循环标签loop label),然后将标签与 breakcontinue 一起使用,使这些关键字应用于已标记的循环而不是最内层的循环。

下面是一个包含两个嵌套循环的示例:

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

从循环返回

loop 的一个用例是重试可能会失败的操作,比如检查线程是否完成了任务。然而你可能会需要将操作的结果从循环中传递给其它的代码。为此,你可以在用于停止循环的 break 表达式添加你想要返回的值;该值将从循环中返回,以便您可以使用它。

例子:

fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;

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

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

while 条件循环

在程序中计算循环的条件也很常见。当条件为真,执行循环。当条件不再为真,调用 break 停止循环。这个循环类型可以通过组合 loopifelsebreak 来实现;如果你喜欢的话,现在就可以在程序中试试。然而,这个模式太常用了,Rust 为此内置了一个语言结构,它被称为 while 循环。示例 3-3 使用了 while 来程序循环 3 次,每次数字都减 1。接着在循环结束后,打印出另一个信息并退出。

例子:

fn main() {
    let mut number = 3;

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

        number -= 1;
    }

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

这种结构消除了很多使用 loopifelsebreak 时所必须的嵌套,这样更加清晰。当条件为真就执行,否则退出循环。

使用 for 遍历集合

先看使用 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;
    }
}

数组中的 5 个元素全部都如期被打印出来。尽管 index 在某一时刻会到达值 5,不过循环在其尝试从数组获取第 6 个值(会越界)之前就停止了。

但是这个过程很容易出错;如果索引值或测试条件不正确会导致程序 panic。例如,如果将 a 数组的定义更改为包含 4 个元素,但忘记将条件更新为while index < 4,则代码会出现 panic。这也使程序更慢,因为编译器增加了运行时代码来对每次循环进行条件检查,以确定在循环的每次迭代中索引是否在数组的边界内。

作为更简洁的替代方案,可以使用 for 循环来对一个集合的每个元素执行一些代码。

例子:

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

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

这段代码增强了代码安全性,并消除了可能由于超出数组的结尾或遍历长度不够而缺少一些元素而导致的 bug。

使用 for 循环的话,就不需要惦记着在改变数组元素个数时修改其他的代码了。

for 循环的安全性和简洁性使得它成为 Rust 中使用最多的循环结构。即使是在想要循环执行代码特定次数时,例如示例 3-3 中使用 while 循环的倒计时例子,大部分 Rustacean 也会使用 for 循环。这么做的方式是使用 Range,它是标准库提供的类型,用来生成从一个数字开始到另一个数字之前结束的所有数字的序列。

下面是一个使用 for 循环来倒计时的例子,它还使用了一个我们还未讲到的方法,rev,用来反转区间(range):

文件名: src/main.rs

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

这段代码更好一些,不是吗?

另附示例:

fn main() {
    // rev():逆序 step_by(2):步长2
    for i in (1..10).rev().step_by(2) {
        println!("{}", i);
    }

    // step_by 文档示例
    let a = [0, 1, 2, 3, 4, 5];
    let mut iter = a.iter().step_by(2);

    assert_eq!(iter.next(), Some(&0));
    assert_eq!(iter.next(), Some(&2));
    assert_eq!(iter.next(), Some(&4));
    assert_eq!(iter.next(), None);

    // 华氏度和摄氏度换算
    let mut temp = String::new();
    println!("Please enter a temperature value with units(C/F): ");
    std::io::stdin().read_line(&mut temp).expect("Incorrect input format.");

    let temp = temp.trim();
    let len = temp.len();
    let tup = temp.split_at(len - 1);
    let (temp, unit) = tup;
    let temp: f64 = temp.parse().expect("Incorrect input format.");
    if unit.eq("C") {
        let f = 32.0 + temp * 1.8;
        println!("Converted: {}F", f);
    } else if unit.eq("F") {
        let c = (temp - 32.0) / 1.8;
        println!("Converted: {}C", c);
    } else {
        println!("Incorrect input format.");
    }

    // 生成 n 阶斐波那契数列
    fn fab(n: u32) -> u32 {
        if n == 0 || n == 1 {
            return 1;
        }

        return fab(n - 2) + fab(n - 1);
    }

    for i in 0..10 {
        print!("{} ", fab(i));
    }
    println!();
}

补充

match

match x {
    // 可以使用 | 匹配多个模式
    case1 | case3 | case4 => {
        // 执行某些操作
    },
    case2 => {
        // 执行不同的操作
    },
    _ => {
        // 默认行为
    }
}

if let

fn main() {
    let some_option_value: Option<u32> = Some(3);
    // 这种写法可能有点违背直觉,但是=在这里代表模式匹配而不是赋值,Some(3)是要匹配的值
    if let Some(3) = some_option_value {
        println!("匹配到数字3!");
    } else {
        println!("没有匹配到数字3!");
    }
}

while let

fn main() {
    let mut some_numbers = vec![1, 2, 3].into_iter();
    // 同上 if let
    while let Some(num) = some_numbers.next() {
        println!("获得数字:{}", num);
    }
}
文章作者: Billy
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 billy blog
笔记 Rust
喜欢就支持一下吧