Skip to content

本小节目标

通过循环完成完整的猜数游戏.

代码

当前的代码与上节相比有较大改动, 请复制到本地, 稍后讲解:

rust
use rand::random_range;
use std::{cmp::Ordering, io};

fn main() {
    println!("请猜测一个数");

    let secret_num = random_range(1..=100);

    println! ("{}", loop {
        let mut guess = String::new();
        io::stdin().read_line(&mut guess).unwrap();
        let guess = guess.trim();

        let result = match guess.cmp(&secret_num.to_string()) {
            Ordering::Less => "太小了",
            Ordering::Greater => "太大了",
            Ordering::Equal => "猜对了",
        };

        println!("你猜的是: {}, {}.", guess, result);

        if result == "猜对了" {
            break "结束猜测.";
        }
    })
}

loop 循环

代码中出现了新的关键字 loop, 使用语句

rust
loop {
    /* 山有扶苏, 隰有荷华. */
}

将会一遍一遍地顺序运行大括号定界的语句块. 代码中, 我们通过 loop 循环实现让用户重复猜测数字并判断比较情况.

loop 循环可被 break 关键字跳出. 此外虽然本处代码未提及, 但还有一个类似的关键字 continue, 将会仅跳过本次循环的剩余部分而进入下一次循环, 而非直接跳出循环体.

所有语句都是表达式

我们在循环体内定义了 result 为将要打印的提示信息, 通过 match 模式匹配根据不同的比较结果返回不同的结果, 此处

rust
match guess.cmp(&secret_num.to_string()) {
    Ordering::Less => "太小了",
    Ordering::Greater => "太大了",
    Ordering::Equal => "猜对了",
}

的返回值类型就是 &str, 之后会知道这就是字符串字面量的类型. 将这团东西看做一个黑盒 some_function_about(&guess, &secret_num), 那么我们的定义赋值语句就可以视作

rust
let result = some_function_about(&guess, &secret_num);

这么看待的目的在于末尾的分号 ;: 模式匹配的大括号结束时, 赋值语句并未关闭, 而额外通过一个 ; 关闭.

再来看 break 关键字后跟随的一个字符串字面量:

rust
break "结束猜测.";

loop 循环体同样拥有返回值, 其内容就决定以 break 关键字. 在当前代码中, 除非被 .unwrap() 截胡, 否则 loop 循环结束时总会由 break 返回字符串字面量 "结束猜测.".

进一步说明

除了后面跟着某个值, break 关键字同样可以直接以 ; 关闭:

rust
loop {
    break;
}

此时并不是说 loop 循环体没有返回值, 而是说其返回值类型为空, 写作 (). 这并非某种特例, 事实上, 所有以分号 ; 结尾的表达式, 或者叫做语句, 均返回 () 类型.