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_game
     Created 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 语言的 #includeJava、PythonimportPerluse,等等。
    总之,这就是 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 类型的枚举值可以是 OkErr:当为 Ok 时,调用 expect()
    做任何操作,而当为 Err 时,调用 expect() 将打印信息 Failed to read line

编译、运行:

$ cargo build
   Compiling guessing_game v0.1.0 (/home/bill/Study/rust/guessing_game)
    Finished dev [unoptimized + debuginfo] target(s) in 4.42s
$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `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/HEAD
  Downloaded 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 57s
   Compiling proc-macro2 v1.0.86
   Compiling unicode-ident v1.0.12
   Compiling libc v0.2.158
   Compiling cfg-if v1.0.0
   Compiling byteorder v1.5.0
   Compiling quote v1.0.37
   Compiling syn v2.0.56
   Compiling getrandom v0.2.15
   Compiling rand_core v0.6.4
   Compiling zerocopy-derive v0.7.35
   Compiling zerocopy v0.7.35
   Compiling ppv-lite86 v0.2.20
   Compiling rand_chacha v0.3.1
   Compiling rand v0.8.5
   Compiling 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 安装为更新版本,系统自带的版本可能导致无法下载第三方库。可通过如下方式安装 Rust 最新版本:

$ export RUSTUP_UPDATE_ROOT=https://mirrors.aliyun.com/rustup/rustup
$ export RUSTUP_DIST_SERVER=https://mirrors.aliyun.com/rustup
$ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh

好了,一切就绪,是时候修改代码引入目标数随机生成,以及用户输入数字比较判定功能了。修改后的代码如下:

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 = num
			Err(_) => 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.01s
     Running `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 组合,不难理解。
  • 同名变量隐藏(Shadowing)
    代码里面两次定义了变量 guess,这有点类似 Python 的同名变量,它们彼此可以有不同的类型,后一变量定义会隐藏前一定义。
  • loop 循环
    loop 关键字,组织了循环结构,类似于其它语言的 while (1) { ... }

4. 参考资料

[1] The Rust Programming Language

你可能感兴趣的:(Rust,rust)