2.4.0. Key Points of This Section
This is the final part of the number guessing game. The key points in this section are:
loopbreakcontinue- Flexible use of
match - How to handle enum types
2.4.1. Game Objectives
- Generate a random number between 1 and 100
- Prompt the player to enter a guess
- After the guess, the program will tell whether the guess is too big or too small
- Keep asking repeatedly. If the guess is correct, print a congratulatory message and exit the program (covered in this section)
2.4.2. Code Implementation
Step 1: Implementing a Loop
In the previous code, we implemented the comparison for one input. Next, we need to implement repeated prompts and repeated comparisons until the user guesses the correct number.
Here is the code up to the section before this one:
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);
}
The part of the code that we need to execute repeatedly is the section from prompting, to comparison, to outputting the comparison result. In terms of code, that is:
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 provides the keyword loop for an infinite loop, and its structure is as follows:
loop {
// Write the code here that you want to loop indefinitely
// Write code here that wants to loop indefinitely
}
You only need to put the code that needs to be executed repeatedly into this structure:
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: The Condition for Exiting the Program
But note that at this point, the program can repeatedly ask for input, but it will keep asking forever and will never exit. Logically, after the user guesses correctly and the program prints the message, it should stop asking. This is where the keyword break, which is used to exit the loop, is needed. Just put it after the Ordering::Equal branch (the concept of branches was explained in the previous article, so it will not be repeated here). Remember: if you want to execute multiple lines of code in one branch, you need to wrap the code block in {}.
match guess.cmp(&range_number){
Ordering::Less => println!("Too small"),
Ordering::Greater => println!("Too big"),
Ordering::Equal => {
println!("You win");
break;
}
}
Step 3: Handling Invalid Input
There is still one problem with this code: if the user's input is not an integer, then .parse() will return Err, and once .expect() receives it, the program will terminate immediately. But the correct logic should be that if the input is invalid, the program should print an error message and then let the user enter again.
So what should we do? In 2.1 Number Guessing Game Pt.1 Single Guess, it was mentioned that the return value of .parse() is an enum type. If conversion succeeds, the return value is: Ok + the converted content; if it fails, the return value is: Err + the reason for failure. So where have we talked about enum types before? That's right—in the previous article, we mentioned the enum type Ordering, and in that article we used match to handle the cases of greater than, less than, and equal to. So here, we can also use match to handle the return value of .parse(), performing different operations in different cases. Specifically: if the conversion succeeds, continue executing; if it fails, skip the following code and execute the next loop iteration. In Rust, the keyword for skipping the current iteration of a loop is continue, just like in other languages.
So how exactly should the code be changed? Replace this line:
let guess:u32 = guess.trim().parse().expect("Please enter a number");
with:
let guess:u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
Ok(num) => num: This branch handles the case where the conversion succeeds, that is, when the return value isOkplus the converted content.Okis one variant of this enum type. Inside the()afterOkis the converted content carried by the enum (u32). Writingnuminside the()here means binding the converted content tonum. The value ofnumwill be passed as the result of thematchexpression and then finally assigned toguess.Err(_) => continue: This branch is used to handle the case where the conversion fails, that is, when the return value isErrplus the reason for failure.Erris a variant of the enum type. Inside the()afterErris the reason for the failure carried by the enum (&str). Writing_inside the()means that we do not care about the specific error message; we only need to know that it isErr.
Using match here instead of .expect() to handle errors is a common Rust approach to error handling.
2.4.3. Code Result
Below is the complete code:
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(_) => 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);
}
Result:

Top comments (0)