2. 猜数字游戏
2.1 代码结构
// prelude,导入标准库
use std::io;
fn main() {
println!("猜数游戏!");
// 可变变量接收用户输入
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("读取行失败");
println!("你猜测的数字是:{}", guess);
}上述代码用于实现读取用户输入的功能,io::stdin().read_line(&mut guess) 用于读取用户输入的内容,&mut guess 表示将用户输入的内容存储到 guess 变量中。
2.2 使用 rand 库生成随机数
由于 Rust 并没有提供生成随机数的标准库,我们需要使用第三方库 rand 来生成随机数。
在 Cargo.toml 中添加 rand 库的依赖:
[dependencies]
rand = "^0.8.5"修改 main.rs 文件:
use rand::Rng;
use std::io;
fn main() {
println!("猜数游戏!");
let secret_number = rand::thread_rng().gen_range(1..101);
loop {
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("读取行失败");
let guess: u32 = guess.trim().parse().expect("请输入一个数字");
println!("你猜测的数字是:{}", guess);
match guess.cmp(&secret_number) {
std::cmp::Ordering::Less => println!("太小了!"),
std::cmp::Ordering::Greater => println!("太大了!"),
std::cmp::Ordering::Equal => {
println!("你赢了!");
break;
},
}
}
}我们还可以使用 match 语句对错误处理进行一些优化:
use rand::Rng;
use std::io;
use std::io::Write;
fn main() {
println!("猜数游戏!");
let secret_number = rand::thread_rng().gen_range(1..101);
loop {
print!("请输入数字:");
io::stdout().flush().unwrap();
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("读取行失败");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("请输入一个数字!");
continue;
},
};
println!("你猜测的数字是:{}", guess);
match guess.cmp(&secret_number) {
std::cmp::Ordering::Less => println!("太小了!"),
std::cmp::Ordering::Greater => println!("太大了!"),
std::cmp::Ordering::Equal => {
println!("你赢了!");
break;
}
}
}
}我们先解释一些语句。
use 语句用于导入库,rand::Rng 是一个第三方库,用于生成随机数。
use rand::Rng;
use std::io;let 语句用于声明变量,使用 mut 关键字声明可变变量(Mutable Variable)。
1..101 是一个范围(Range),表示从 1 到 100(不包含 101)的范围。这是 Rust 的一个新语法区间表达式,如果想包含 101 可以写作 1..=100。
parse().expect("请输入一个数字"); 是一个错误处理行为,如果用户输入的不是数字,程序会抛出异常。
.parse() 在此返回一个 Result<u32, ParseIntError> 类型,Result 是一个枚举类型,Ok 表示成功,Err 表示失败。
2.3 深入理解猜数字游戏
让我们深入理解上面代码中的一些关键概念:
变量遮蔽(Shadowing)
在代码中,我们使用了变量遮蔽:
let mut guess = String::new();
// ...
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};第一个 guess 是可变的 String 类型,第二个 guess 是不可变的 u32 类型。这是 Rust 的一个特性,允许我们重用变量名,同时改变其类型和可变性。这比使用不同的变量名(如 guess_str 和 guess_num)更简洁。
match 表达式
match 是 Rust 中强大的控制流运算符,允许我们将值与一系列模式进行比较:
match guess.cmp(&secret_number) {
std::cmp::Ordering::Less => println!("太小了!"),
std::cmp::Ordering::Greater => println!("太大了!"),
std::cmp::Ordering::Equal => {
println!("你赢了!");
break;
}
}cmp 方法返回 Ordering 枚举,它有三个变体:Less、Greater 和 Equal。match 表达式会尝试匹配每一个分支,执行匹配成功的分支。
错误处理
Rust 强制我们处理可能出现的错误。在猜数字游戏中,我们处理了两种错误:
- 读取输入失败:使用
.expect()方法 - 解析失败:使用
match表达式
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("请输入一个数字!");
continue;
},
};这种错误处理方式让程序更加健壮,不会因为用户输入了非数字字符而崩溃。
循环控制
游戏使用了 loop 无限循环,并通过 break 和 continue 控制流程:
break:当玩家猜对数字时退出循环continue:当输入无效时跳过本次循环,继续下一次循环
依赖管理
在 Cargo.toml 中,我们使用语义化版本(Semantic Versioning)指定依赖:
rand = "^0.8.5"^0.8.5 表示任何与 0.8.5 兼容的版本,即 >= 0.8.5 且 < 0.9.0 的版本。这确保了我们能获取 bug 修复和小的功能更新,同时避免不兼容的更改。
2.4 改进猜数字游戏
我们可以对游戏进行一些改进,使其更加完善:
添加猜测次数限制
use rand::Rng;
use std::io;
use std::io::Write;
fn main() {
println!("猜数游戏!");
let secret_number = rand::thread_rng().gen_range(1..=100);
let max_attempts = 10;
let mut attempts = 0;
loop {
if attempts >= max_attempts {
println!("很遗憾,你已经用完了所有机会!正确答案是:{}", secret_number);
break;
}
attempts += 1;
print!("第 {} 次猜测,请输入数字(1-100):", attempts);
io::stdout().flush().unwrap();
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("读取行失败");
let guess: u32 = match guess.trim().parse() {
Ok(num) => {
if num < 1 || num > 100 {
println!("请输入 1 到 100 之间的数字!");
attempts -= 1; // 不计入无效输入
continue;
}
num
}
Err(_) => {
println!("请输入一个有效的数字!");
attempts -= 1; // 不计入无效输入
continue;
}
};
match guess.cmp(&secret_number) {
std::cmp::Ordering::Less => {
println!("太小了!还剩 {} 次机会。", max_attempts - attempts);
}
std::cmp::Ordering::Greater => {
println!("太大了!还剩 {} 次机会。", max_attempts - attempts);
}
std::cmp::Ordering::Equal => {
println!("恭喜你,猜对了!你用了 {} 次机会。", attempts);
break;
}
}
}
}这个改进版本添加了以下功能:
- 限制猜测次数为 10 次
- 验证输入范围(1-100)
- 显示剩余机会
- 无效输入不计入猜测次数
- 游戏结束时显示正确答案(如果未猜中)
通过这个简单的猜数字游戏,我们学习了 Rust 的许多核心概念,为深入学习 Rust 打下了基础。