编写一个猜数游戏

2023/2/11

# 处理一次猜测

猜数游戏的第一部分会请求用户进行输入,并检查该输入是否满足预期的格式

use std::io;

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

    println!("请输入你猜测的数字");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess).expect("阅读行失败");

    println!("你猜测的数字是:{}",guess);
}

use std::io; 把标准库(也就是所谓的 std)中的 io 模块引入当前的作用域中

# 使用变量来存储值

在 Rust 中,变量都是默认不可变的

let foo = 5; // foo是不可变的
let mut bar = 5; // bar是可变的

# 注意

1.函数String::new后返回的结果:一个新的 String 实例。String 是标准库中的一个字符串类型,它在内部使用了 UTF-8 格式的编码并可以按照需求扩展自己的大小。

2.我们在程序的第一行使用了语句 use std::io。现在,我们将调用 io 模块中的关联函数 stdin:

use std::io;
io::stdin().read_line(&mut guess).expect("阅读行失败");

假如你没有在程序的开始处添加 use std::io;行那么就需要将这个函数调用修改为 std::io::stdin。

std::io::stdin().read_line(&mut guess).expect("阅读行失败");

3.Result 类型来处理可能失败的情况

.expect("阅读行失败");

# 运行

> cargo run
猜一个数字
请输入你猜测的数字
11
你猜测的数字是:11

# 生成一个随机数字

为了保证一定的可玩性,并使每局游戏都有不同的体验,这个生成的保密数字将会是随机的。为了让游戏不会过分困难,我们可以把这个随机数字限制在 1 到 100 之间。Rust 团队并没有把类似的随机数字生成功能内置到标准库中,而是选择将它作为 rand 包(rand crate)提供给用户。

Cargo.toml

[dependencies]

rand = "0.3.14"

运行 cargo build 时,Cargo 就会自动更新注册表中所有可用包的最新版本信息

use std::io;

use rand::Rng;

fn main() {
    println!("猜一个数字");
    let secret_number = rand::thread_rng().gen_range(1, 10);
    println!("请输入你猜测的数字");
    let mut guess = String::new();

    io::stdin().read_line(&mut guess).expect("阅读行失败");

    println!("你猜测的数字是:{}", guess);
    println!("随机数字是:{}", secret_number);
}

use rand::Rng。这里的Rng是一个 trait(特征),它定义了随机数生成器需要实现的方法集合。为了使用这些方法,我们需要显式地将它引入当前的作用域中。

第一行中的函数rand::thread_rng会返回一个特定的随机数生成器:它位于本地线程空间,并通过操作系统获得随机数种子。随后,我们调用了这个随机数生成器的方法gen_range。这个方法是在刚刚引入作用域的Rng rait中定义的,它接收两个数字作为参数,并生成一个范围在两者之间的随机数。

# 运行

> cargo run
猜一个数字
请输入你猜测的数字
8
你猜测的数字是:8

随机数字是:2

# 比较猜测数字与保密数字

随机生成的保密数字,还有一个用户输入的猜测数字。接下来,在代码比较这两个数字


 








 
 


 
 
 
 
 


use rand::Rng;
use std::cmp::Ordering;
use std::io;

fn main() {
    println!("猜一个数字");
    let secret_number = rand::thread_rng().gen_range(1, 10);
    println!("请输入你猜测的数字");
    let mut guess = String::new();
    io::stdin().read_line(&mut guess).expect("阅读行失败");
    // 将String类型转换为数值类型
    let guess: u32 = guess.trim().parse().expect("请输入一个数字");
    println!("随机的数字是:{}", secret_number);
    println!("你猜测的数字是:{}", guess);
    match guess.cmp(&secret_number) {
        Ordering::Less => println!("数字太小了"),
        Ordering::Greater => println!("数字太大了"),
        Ordering::Equal => println!("你赢了"),
    }
}

1.Rust 允许使用同名的新变量 guess 来隐藏(shadow)旧变量的值。

# 运行

> cargo run
猜一个数字
请输入你猜测的数字
4
随机的数字是:6
你猜测的数字是:4
数字太小了

# 使用循环来实现多次猜测

在 Rust 中,loop 关键字会创建一个无限循环。我们可以将它加入当前的程序中,进而允许玩家反复地进行猜测抉择:









 






 
 
 
 





 
 
 





use rand::Rng;
use std::cmp::Ordering;
use std::io;

fn main() {
    println!("猜一个数字");
    let secret_number = rand::thread_rng().gen_range(1, 10);

    loop {
        println!("请输入你猜测的数字");

        let mut guess = String::new();

        io::stdin().read_line(&mut guess).expect("阅读行失败");
        // 将String类型转换为数值类型
        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,
        };

        println!("你猜测的数字是:{}", guess);
        match guess.cmp(&secret_number) {
            Ordering::Less => println!("数字太小了"),
            Ordering::Greater => println!("数字太大了"),
            Ordering::Equal => {
                println!("你赢了");
                break;
            }
        }
    }
}

假如parse没能将字符串解析为数字,那么它将返回一个包含了具体错误信息的Err值。这个值会因为无法匹配Ok(num)模式而跳过match表达式的第一个分支,并匹配上第二个分支中的Err(_)模式。这里的下画线_是一个通配符,它可以在本例中匹配所有可能的Err值,而不管其中究竟有何种错误信息。因此,程序会继续执行第二个分支中的代码:continue,这条语句会使程序直接跳转至下一次循环,并再次请求玩家猜测保密数字。这样,程序便忽略了parse可能会触发的那些错误。