Rust 学习笔记 2:猜数字游戏
上一篇:Rust 学习笔记 1:编译运行环境的构建
文章目录
- 1. 前言
- 2. 背景
- 3. 猜数字游戏
- 3.1 概述
- 3.2 实现
- 4. 参考资料
1. 前言
限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。
2. 背景
本文基于 Rust 文档 Programming a Guessing Game 翻译整理而成。
3. 猜数字游戏
3.1 概述
程序随机生成一个数字,并告知用户数据所处区间范围;用户根据区间范围随意输入一个数字,程序读取用户输入的数字,将其和目标数字对比,并提示数字是大了还是小了;然后用户根据提示继续输入,直到猜中目标数字为止。
从前面的描述可以看到,猜数字游戏,程序需要处理:
- 随机数字生成
- 读取用户输入
- 数字比较
- 输出提示信息
- 循环逻辑
3.2 实现
首先,我们给出一个初始版本,这个版本没有随机生成目标数字、以及进行数字比较的功能,它仅仅是打印用户输入的数字。先用 Cargo 构建一个代码工程:
$ cargo new guessing_gameCreated binary (application) `guessing_game` package
然后对代码文件 guessing_game/src/main.rs 进行编辑,其内容如下:
use std::io;fn main() {println!("Guess the number!");println!("Please input your guess.");let mut guess = String::new();io::stdin().read_line(&mut guess).expect("Failed to read line");println!("You guessed: {}", guess);
}
这里有几个需要了解的新知识点:
- use std::io;
这类似于C语言的#include,Java、Python的import,Perl的use,等等。
总之,这就是Rust对其它库接口的导入方式。这里是导入了Rust标准 IO 库。 - String::new();
::在这里用来引用某个作用域内的接口。这里创建了一个空的字符串类型对象。 - let mut guess = String::new();
let关键字用来定义变量,而mut是使得变量可以修改(mutable)。
默认情况下,let定义的变量是不可修改的(immutable)。
我们注意到,Rust定义变量,没有显式指定类型。 - io::stdin()
构建了一个std::io::Stdin实例对象。 - io::stdin().read_line(&mut guess)
调用std::io::Stdin实例对象的接口函数read_line(),用于读取一行用户输入;
read_line(&mut guess)的参数加&表示使用guess的引用,但默认情况下,
对象的引用是只读的,及read_line()无法通过guess的引用修改guess,
这时候加上mut修饰,使得read_line()可以通过guess的引用修改guess。 - io::stdin().read_line(&mut guess).expect(“Failed to read line”);
std::io::Stdin实例对象的接口函数read_line()返回一个 Result 类型的枚举值;
而Result类型的枚举值可以是Ok和Err:当为Ok时,调用expect()不做任何操
作,而当为Err时,调用expect()将打印信息Failed to read line。
编译、运行:
$ cargo buildCompiling guessing_game v0.1.0 (/home/bill/Study/rust/guessing_game)Finished dev [unoptimized + debuginfo] target(s) in 4.42s
$ cargo runFinished dev [unoptimized + debuginfo] target(s) in 0.03sRunning `target/debug/guessing_game`
Guess the number!
Please input your guess.
8
You guessed: 8
到此,初始版本已经构建完毕。接下来,我们引入随机数模块,随机生成 [1,100] 的数字,并增加对用户输入数字的判定。Rust 的标准库不包含随机数模块,随机数模块是一个第三方库。Rust 引入第三方库的方式是通过编辑 Cargo 构建工程的 Cargo.toml 文件的 [dependencies] 段。按如下编辑 guessing_game/Cargo.toml 的 [dependencies] 段引入 rand 库:
[dependencies]
rand = "0.8.5"
不改变任何代码,再次构建工程,会发现构建过程下载了 rand 库:
$ cargo build
warning: `/home/XXX/.cargo/config` is deprecated in favor of `config.toml`
note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml`
warning: `/home/XXX/.cargo/config` is deprecated in favor of `config.toml`
note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml`Updating `tuna` index
remote: Enumerating objects: 1102, done.
remote: Counting objects: 100% (292/292), done.
remote: Total 1102 (delta 292), reused 292 (delta 292), pack-reused 810
Receiving objects: 100% (1102/1102), 527.38 KiB | 0 bytes/s, done.
Resolving deltas: 100% (809/809), completed with 130 local objects.
From https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index* [new ref] -> origin/HEADDownloaded cfg-if v1.0.0 (registry `tuna`)Downloaded rand_core v0.6.4 (registry `tuna`)Downloaded rand_chacha v0.3.1 (registry `tuna`)Downloaded byteorder v1.5.0 (registry `tuna`)Downloaded quote v1.0.37 (registry `tuna`)Downloaded unicode-ident v1.0.12 (registry `tuna`)Downloaded zerocopy-derive v0.7.35 (registry `tuna`)Downloaded proc-macro2 v1.0.86 (registry `tuna`)Downloaded rand v0.8.5 (registry `tuna`)Downloaded zerocopy v0.7.35 (registry `tuna`)Downloaded syn v2.0.56 (registry `tuna`)Downloaded libc v0.2.158 (registry `tuna`)Downloaded 12 crates (1.5 MB) in 2m 57sCompiling proc-macro2 v1.0.86Compiling unicode-ident v1.0.12Compiling libc v0.2.158Compiling cfg-if v1.0.0Compiling byteorder v1.5.0Compiling quote v1.0.37Compiling syn v2.0.56Compiling getrandom v0.2.15Compiling rand_core v0.6.4Compiling zerocopy-derive v0.7.35Compiling zerocopy v0.7.35Compiling ppv-lite86 v0.2.20Compiling rand_chacha v0.3.1Compiling rand v0.8.5Compiling guessing_game v0.1.0 (/home/bill/Study/rust/guessing_game)Finished `dev` profile [unoptimized + debuginfo] target(s) in 3m 02s
如果你在国内,大概率会下载失败。如果遇到了这种情况,可参考下列链接配置 Crates.io 来解决问题:
- 4.1. 配置 Cargo 国内镜像源
- rsproxy.cn - 字节跳动新的 Rust 镜像源
- Cargo设置
笔者使用的 ~/.cargo/config 内容如下,读者可作为参考:
[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"replace-with = 'tuna'
#replace-with = 'ustc'
#replace-with = 'sjtu'
#replace-with = 'rsproxy'[source.tuna]
registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"[source.sjtu]
registry = "https://mirrors.sjtug.sjtu.edu.cn/git/crates.io-index"[source.rsproxy]
registry = "https://rsproxy.cn/crates.io-index"[source.rustcc]
registry = "git://crates.rustcc.cn/crates.io-index"[net]
git-fetch-with-cli=true
另外,还需要将 rustc 安装为更新版本,系统自带的版本可能导致无法下载第三方库。好了,一切就绪,是时候修改代码引入目标数随机生成,以及用户输入数字比较判定功能了。修改后的代码如下:
use rand::Rng;
use std::io;
use std::cmp::Ordering;fn main() {println!("Guess the number!");let secret_number = rand::thread_rng().gen_range(1..=10);println!("The secret number is at range: [1, 10]");loop {println!("Please input your guess.");let mut guess = String::new();io::stdin().read_line(&mut guess).expect("Failed to read line");//let guess: u32 = guess.trim().parse().expect("Please type a number!");let guess: u32 = match guess.trim().parse() {Ok(num) => num, // guess = numErr(_) => continue, // _ to match all};println!("You guessed: {}", guess);match guess.cmp(&secret_number) {Ordering::Less => println!("Too small!"),Ordering::Greater => println!("Too big!"),//Ordering::Equal => println!("You win!"),Ordering::Equal => {println!("You win!");break; // exit the loop}}}
}
编译、运行:
$ cargo build
warning: `/home/bill/.cargo/config` is deprecated in favor of `config.toml`
note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml`
warning: `/home/bill/.cargo/config` is deprecated in favor of `config.toml`
note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml`Compiling guessing_game v0.1.0 (/home/bill/Study/rust/guessing_game)Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.21s
bill@bill-virtual-machine:~/Study/rust/guessing_game$ cargo run
warning: `/home/bill/.cargo/config` is deprecated in favor of `config.toml`
note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml`
warning: `/home/bill/.cargo/config` is deprecated in favor of `config.toml`
note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml`Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01sRunning `target/debug/guessing_game`
Guess the number!
The secret number is at range: [1, 10]
Please input your guess.
5
You guessed: 5
Too small!
Please input your guess.
8
You guessed: 8
You win!
相对第一版代码,修改后的代码又引入了几个新的知识点,总结如下:
- match 表达式
这类似于 C 语言的 switch / case 组合,不难理解。 - 同名变量覆盖
代码里面两次定义了变量guess,这有点类似Python的同名变量,它们彼此可以有不同的类型,后一变量定义会隐藏前一定义。 - loop 循环
loop关键字,组织了循环结构,类似于其它语言的while (1) { ... }。
4. 参考资料
[1] The Rust Programming Language
