Rust - Part 2
Chapter 2:
1. Setup:
Let's create a number guessing game. Let's create a new package and add some print statements.
cargo new guessing_game
cd guessing_game
use std::io;
fn main() {
println!("Guess the number");
println!("Your guess?");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed");
println!("You guessed: {}", guess);
}
use std::io
is like importing packages in java
println! is a macro, we'll get to that later.
In rust values are immutable by default, so we need to add the mut before declaration
:: is equivalent to dot operator in other languages to call functions, but only "static" like functions. If we are going to be calling it an object, instead of the String type itself, then we can use the dot operator.
io::stdin()
means we are calling stdin method of io module, and again similar to other languages, if we don't add the use std::io in the first line, we can still call the stdin method, but this time we have to call it like std::io::stdin()
read_line
takes a String reference as an input, and appends the standard input to the variable.
I would've guessed that the input to pass would be &guess
, but instead we'll be passing &mut guess
(The book says that this will be explained in the later part).
The return type is of io::Result
, which is nothing but an enum with different variants - as far as Result goes, it's just Ok
and Err
.
If the result of read_line is Err, program will crash and display the error message we have passed it as an argument, but if the result is Ok, expect will take the value that Ok is holding and return just the value.
If we skip the expect
part, the program will be compiled, but we will get a warning.
Print line is self explanatory, you can also add multiple arguments.
2. Adding rand cargo
The next part is to generate a random number, but rust by default doesn't have rand number generator in standard library, so we are going to use a crate or dependency in other words.
If you want to update the version, you can do so by running cargo update
, during which your Cargo.lock file will be updated.
Regarding traits, we won't know which one to use, so it's always recommended to read the documentation or run the above command.
Let's add the package for rand by adding this use rand::Rng and let's also add
let secret_number = rand::thread_rng().gen_range(1..101);
If you notice, we aren't using Rng
in this line, while it is weird, apparently, Rng
is not really a module, but a "trait" which will be explored in the future chapters, and for us to generate a random number this Rng needs to be in scope, weird.
rand::thread_rng
function gives a random number generator, seeded by the OS.
1..101
means we are specifying a range, lower bound inclusive, upper bound exclusive or we can also use 1..=100
If we want to read the documentation about the crates we are using we can run cargo doc --open
and select the crate from the left bar.
You can try printing the random number if you want, now on to the next part, to compare if the input is smaller or greater than the secret number.
3. Comparing
For this part we need to use another use statement - use std::cmp::Ordering;
and add the following code. Ordering is also similar to Result enum and has the following 3 enum valuies
match guess.cmp(&secret_number) {
Ordering::Less => println!("Small"),
Ordering::Greater => println!("Big"),
Ordering::Equal => println!("GG"),
}
One way to understand this is to think of a switch statement, and each section is called an arm, if one condition matches, other's won't be compared.
There is one problem here, that we will encounter if we build it now, that is guess is String, but secret_number
is an integer.
The reason we didn't explicitly give the type is because rust can understand by looking at the right hand side of the equal operator.
As far as random number there are few types it could've been i32, u32(unsigned) and i64, but rust defaults to i32.
In python or javascript, if we want to change the type of variable, we parse it and assign it to the same variable but that doesn't make sense in, say Java, because when the variable was created we got into a contract saying that I'll pass values that are only of the type I've created, so we end up creating value_string to store the string format.
Rust takes both their sides, and gives us a way to do that - called Shadowing - we can change the variable type, but it should be created anew using let command, so the line will be something like this
let guess: u32 = guess.trim().parse().expect("Not a valid number");
trim removes whitespace, new line characters, this parse is interesting because it parses to the type needed in the left side of the = operator.
4. Loop and finishing the game
This might be a good place to try to run the program. Now there is just one more thing left to do, to keep guessing in a loop, it's easy in rust, you just need to enclose the code in brackets with keyword loop, this will cause an infinite loop.
We now need to break the program if we guess it correctly, let's edit the Ordering::Equal
arm to like this
Ordering::Equal => {
println!("GG");
break;
}
One minor change we can do is not crash the program when there is an invalid input, in order to do that, we should change the expect while parsing to match like the following:
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("Please enter a valid integer, try again");
continue;
}
};
If you've followed till the end, congrats, your end code should be something like this
use std::io;
use rand::Rng;
use std::cmp::Ordering;
fn main() {
println!("Guess the number");
let secret_number = rand::thread_rng().gen_range(1..101);
loop {
println!("Your guess?");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("Please enter a valid integer, try again");
continue;
}
};
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Small"),
Ordering::Greater => println!("Big"),
Ordering::Equal => {
println!("GG");
break;
}
}
}
}
End of Chapter2
Tags · Rust, Tech, Blog