2.4 猜数游戏Pt.4 循环询问
2.4.0 本篇知识点
这是猜数游戏的最后一部分。在本篇中,你将学到:
loop循环breakcontinuematch的灵活使用- 如何处理枚举
2.4.1 游戏目标
- 生成一个 1 到 100 间的随机数
- 提示玩家输入一个猜测
- 猜完之后,程序会提示猜测是太大了还是太小了
- 循环询问。如果猜测正确,那么打印一个庆祝信息,程序退出(本篇会涉及)
2.4.2 代码实现
Step 1:实现循环
在之前的代码中,我们实现了一次输入和比较。接下来,我们需要让程序反复询问和比较,直到用户猜到正确的数字。
以下是截止到 2.3 猜数游戏Pt.3 输入数与随机数的对比 的代码:
use std::io;
use rand::Rng;
use std::cmp::Ordering;
fn main() {
let range_number = rand::thread_rng().gen_range(1..101);
println!("Number Guessing Game");
println!("Guess a number");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("Could not read the line");
let guess: u32 = guess.trim().parse().expect("Please enter a number");
println!("The number you guessed is:{}", guess);
match guess.cmp(&range_number) {
Ordering::Less => println!("Too small"),
Ordering::Greater => println!("Too big"),
Ordering::Equal => println!("You win"),
}
println!("The secret number is: {}", range_number);
}
而我们需要重复执行的代码,就是从提示用户输入,到比较猜测并打印结果的部分:
#![allow(unused)]
fn main() {
println!("Guess a number");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("Could not read the line");
let guess: u32 = guess.trim().parse().expect("Please enter a number");
println!("The number you guessed is:{}", guess);
match guess.cmp(&range_number) {
Ordering::Less => println!("Too small"),
Ordering::Greater => println!("Too big"),
Ordering::Equal => println!("You win"),
}
}
Rust 提供了用于无限循环的关键字 loop,其结构如下:
#![allow(unused)]
fn main() {
loop {
// Write code here that wants to loop indefinitely
}
}
只需要把需要重复执行的代码放入这个结构中即可:
#![allow(unused)]
fn main() {
loop {
println!("Guess a number");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("Could not read the line");
let guess: u32 = guess.trim().parse().expect("Please enter a number");
println!("The number you guessed is:{}", guess);
match guess.cmp(&range_number) {
Ordering::Less => println!("Too small"),
Ordering::Greater => println!("Too big"),
Ordering::Equal => println!("You win"),
}
}
}
Step 2:退出程序的条件
但是需要注意的是,虽然这样实现了循环询问,程序却会一直询问下去、永远不会退出。按逻辑,用户猜对并打印祝贺信息后,程序就应该停止询问。这里就需要用于跳出循环的关键字 break,把它放在 Ordering::Equal 这个分支(分支的概念在 2.3 猜数游戏Pt.3 输入数与随机数的对比 中已有解释,这里不再重复)后面即可。还要记住:如果一个分支需要执行多行代码,就要用 {} 把代码块包起来。
#![allow(unused)]
fn main() {
match guess.cmp(&range_number) {
Ordering::Less => println!("Too small"),
Ordering::Greater => println!("Too big"),
Ordering::Equal => {
println!("You win");
break;
}
}
}
Step 3:错误输入的处理
这段代码还有另一个问题:如果用户的输入不是整数,.parse() 会返回 Err,.expect() 会立即终止程序。正确的行为是打印错误信息,然后让用户再试一次。
这该怎么办呢?在 2.1 猜数游戏Pt.1 一次猜测 中我们见过,read_line() 返回的是一个 Result 枚举。在 2.3 猜数游戏Pt.3 输入数与随机数的对比 里,.parse() 同样返回 Result:如果转换成功,返回值是 Ok 加上转换后的内容;如果失败,返回值是 Err 加上失败原因。那我们之前在哪里用 match 处理过枚举呢?没错,2.3 猜数游戏Pt.3 输入数与随机数的对比 介绍了 Ordering 枚举。在那里,我们用 match 处理了大于、小于和等于的情况。这里同样可以用 match 处理 .parse() 的返回值,并对不同情况执行不同操作:如果转换成功,继续执行;如果失败,打印错误信息,跳过循环体剩余代码并开始下一次迭代。Rust 中跳过当前循环迭代的关键字和其他语言一样,都是 continue。
具体该怎么改代码呢?就是把 let guess: u32 = guess.trim().parse().expect("Please enter a number"); 替换为:
#![allow(unused)]
fn main() {
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("Please enter a number");
continue;
}
};
}
Ok(num) => num:这个分支处理转换成功的情况。返回值是Ok加上转换后的值。Ok是这个枚举的一个变体,Ok后面括号里的值就是转换后的内容(u32)。这里写成num表示把转换后的内容绑定到num,然后num会作为match表达式的结果,最终赋给guess。Err(_) => { ... continue; }:这个分支处理转换失败的情况。Err是枚举变体,Err后面括号里的值是错误值(解析整数时是ParseIntError)。_表示我们不关心错误细节,只需要知道它是Err。这里先打印一条简短提示,再continue进入下一轮循环。
用 match 代替 .expect() 来处理错误,是 Rust 中的常见做法。
2.4.3 代码效果
这是完整的代码:
use std::io;
use rand::Rng;
use std::cmp::Ordering;
fn main() {
let range_number = rand::thread_rng().gen_range(1..101);
println!("Number Guessing Game");
loop {
println!("Guess a number");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("Could not read the line");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("Please enter a number");
continue;
},
};
println!("The number you guessed is:{}", guess);
match guess.cmp(&range_number) {
Ordering::Less => println!("Too small"),
Ordering::Greater => println!("Too big"),
Ordering::Equal => {
println!("You win");
break;
},
}
}
println!("The secret number is: {}", range_number);
}
效果(一次本地运行;你的神秘数字和猜中路径会不同):
Number Guessing Game
Guess a number
10
The number you guessed is:10
Too small
Guess a number
50
The number you guessed is:50
Too small
Guess a number
100
The number you guessed is:100
You win
The secret number is: 100