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 on
String` 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.