0x44:$

Rust Day 2

Chapter two of the Rust book has you create a guessing game similar to a lot of other programming tutorials --I believe that the first program I wrote in Lisp, or maybe Scheme, was a guessing game as well. Because this is mostly just a programming chapter I decided to heavily annotate my code and more or less allow that to speak for me. You can find it on my learning2rust git repo and I will post it at the bottom of this post, but it's long!

A few things I liked, Rust basically forces error handling of some sort. That's cool. I took forever to get around to doing that with Python... Also, cargo keeps getting cooler. This is the output of me calling cargo build after making rand a dependency:

$ cargo build
Updating crates.io index
Downloaded rand v0.3.23
Downloaded libc v0.2.48
Downloaded rand v0.4.6
Compiling libc v0.2.48
Compiling rand v0.4.6
Compiling rand v0.3.23
Compiling guessing_game v0.1.0 (/home/pard68/projects/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 19.05s

Without further ado, the Guessing Game:

//Prelude
//before any code written in this src file runs
//Rust inserts the 'prelude' which is the parts 
//of std lib that are imported into ever program
//technically the following line exists at the start
//of every module:
//use std::prelude::v1::*;

//`use` imports modules needed. In this case we are
//importing the std lib's io package, Ordering, which
//and an rng.
use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    //Prints a new line to stout, recall this is a macro
    println!("Guess the number!");

    //`let` creates a new variable
    //`rand::` allows us to call anything inside
    //the `rand` crate (Rust for module). 
    //`thread_rnd()` is the particular rng we will be using
    //to generate a random number in a given range. Our returned
    //number will be local to the thread this program runs in
    //and seeded by the os. 
    //`.gen_range` method is being called on the rng. This method
    //comes with the `rand` crate. It takes two numbers are
    //arguments and returns a random number between those them.
    //The lower bound is inclusive and the upper is exclusive. 
    let secret_number = rand::thread_rng()
        .gen_range(1, 101);

    //We use `loop` which is an infinite loop to keep 
    //prompting for a number to guess. 
    loop {

        println!("Please input a number.");

        //In Rust variables are immutable by default
        //So we use `mut` to declare this as a mutable 
        //variable. We call that variable 'guess' and 
        //set it to the value of the function `String`
        //`String` returns a new instance of a string,
        //recall we are at a lower level than in say 
        //Python, we need to allocate memory for our strings!
        //The new string provided via `String` is a growable
        //utf-8 string.
        //The `::` actually belongs to `new` and not to `String`
        //`::` says that new is an associative function of 
        //`String`. This is also called 'static method'.
        //`new` creates a new, empty string for us to use.
        let mut guess = String::new();

        //We want to use `stdin` to get user input, so we 
        //need to call it from the `io` module we imported.
        //This line could have also been written as:
        //`std::io::Stdin` and then we could have not added
        //the `use` at the top.
        //`read_line` takes standard input and turns it into
        //a string. We pass it our string `guess`, but we are 
        //actually passing the reference to the string in 
        //memory, this is what the `&` means. We included the 
        //`mut` because a reference is normally immutable.
        //If we wished to keep it immutable we would only need 
        //to pass `&guess`.
        //Lastly, we use `.expect`. It is on a new line merely 
        //for readability and to follow style conventions. 
        //`read_line` returns the stdin, `io::Result`. Result is 
        //for error handling. It returns either an 'Ok' or 
        //an 'Err' with specific data on the error. 
        //`expect` will return the string on an Err and then 
        //the program will crash. This is not proper error 
        //handling in Rust, but `expect` is provided because
        //rustc will warn if a `Result` is returned and not used.
        io::stdin().read_line(&mut guess)
            .expect("Failed to read line");

        //The variable `guess` comes from the user input to stdin
        //and is a String. We have to convert it to an int in order
        //to compare it to `secret_number`. To convert it from a string
        //type to an int type we have to first `trim()` all the whitespace.
        //`trim()` is a method of the string type. 
        //Next we call `parse()` on the string, which is a method that
        //parses a string into some number type. `parse()` can return
        //a bunch of num types, so we specify an unsigned 32-bit int
        //with `let guess: u32`. Because `guess` is being compared
        //to `secret_number` and because we did not specify the 
        //type of `secret_number` Rust will infer that `secret_number`
        //is a u32 so that the types match and it can compare successfully.
        //Finally we use `match` to handle any `Err` results from `Result`. 
        //This will error if, for example, the user's input isn't a number.
        //The match is like a switch that handles the Result enum.
        //We can use this instead of `expect()` to continue our program
        //on an error instead of crashing it.
        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,
        };

        //Show the user what number they input.
        println!("You guess: {}", guess);

        //`match` is an expression in Rust. It is sort of like 
        //switches in other languages. A `match` is made up of arms,
        //each arm is a pattern with accompanying instrucions to be 
        //run if the argument matches that arm's pattern.
        //`Ordering` is a type of the `cmp` method. You can use `cmp`
        //on anything which can be compared. It will return a variant of 
        //the `Ordering` enum, describing the relationship of foo to bar.
        //Lastly, we use `break` to exit out of `loop` when the number is found.
        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        }
    }
}

I may have more comments later, but I feel that my annotation covers the chapter very well. Again, if you would rather check it out on Github or pull it, here's a link to the repo