Guessing game
Programming a guessing game in Rust
Let's create a new project by using Cargo and open the dir in editor:
Guessing Game
And edit src/main.rs:
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let guess = String::new();
}
String is a UTF-8 encoded, growable string from standard library.new()is an associative function onString` which returns an empty string.
You IDE would probably infer the type of guess and annotate it with String. You can annotate your variable explicitly like this:
One thing to note here is that unlike C++/C, in Rust variables are immutable by default. So our guess variable still ain't ready to mutate and store any input and change value from default string. Let's change that in the original code:
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
// a mutable `guess`
let mut guess = String::new();
}
I/O
Standard I/O operations can be supported using by io module in std (standard) library. Let's use it by adding it to top of our code:
stdin function. read_line() will take the input and store in passed buf variable which is supposed to be a mutable reference to String (in our case guess). This allows us to modify it without changing ownership of string. We'll talk about this in depth later:
readline() returns an Enum std::result::Result type:
Result is a type that represent either variantsuccess (Ok) or variant failure (Err).
So to handle the error, we can use the expect() to indicate any error msg otherwise print out the guess variable:
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
println!("You guessed: {}", guess);
Random Number Generation
To generate random numbers we'll need to add a depedency to our Cargo.toml file since std library don't ship with one:
cargo build command, which will compile and bring any new dependecies as described by Cargo.toml and also other libs that the new dependency depends on.
Let "import" this dependency as we learned previously, or in Rust terms, bring into scope of current file. We'll be bringing Rng trait from rand library which defines methods that random number generators.
Let's create a new variable to store our randomly generated number and check it's value by running our program using command cargo run:
fn main() {
println!("Guess the number");
// gen_range generates a number between 1 and 100
let secret_number = rand::thread_rng().gen_range(1..101);
println!("The secret number is: {}", secret_number);
...
}
If we wanted to create inclusive range we could have done something like this:
Compare the guess and secret_number
To do that let's bring Ordering into scope. Ordering is an enum that is a result of two things being compared, viz: Less, Equal, Greater:
Continuing at the bottom, we'll use cmp() at match all possible variants of Ordering enum using match keyword:
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too bug!"),
Ordering::Equal => println!("You win!"),
}
}
But, this will give an error since guess is of type String whereas secret_number is an int. So we'll need to convert our guess to int.
// perform this just after read_line()
// shadowing `guess` of type String
let guess: u32 = guess.trim().parse()
.expect("Please type a number");
Game Loop
Let's add a loop, so that user can keep guessing until they get the right number.
fn main() {
loop {
println!("Guess the number!");
// gen_range generates a number between 1 and 100
let secret_number = rand::thread_rng().gen_range(1..101);
println!("The secret number is: {}", secret_number);
println!("Please input your guess.");
// a mutable `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");
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too bug!"),
// exit loop if found the match
Ordering::Equal => {
println!("You win!");
brak;
},
}
}
}
Ok, that's almost done!
Handling invalid inputs
Currently, our program panics if we user gives String as input. We want user to keep prompting to input a number instead of panicking.
We can modify our parse() when parsing guess from String to u32 as it returns a Result enum which can be either Ok or Err with _ to catch any err value and continue no matter what.