Based on “The Rust Programming”

Programming a Guessing Game

  • rust has a few things that will be imported automatically into every rust program, called prelude

  • use use statment bring items into program that are not included in prelude

  • use let statement to create a variable;

  • variables are immutable in rust by default, add mut before variable name

  • :: in String::new() indicated new is an function associated with String type.

  • if library io hasn’t been inported with use std::io at the beginning of the program, you could still use the function with std::io::stdin

  • & indicate this argument is a reference, which give you a way to let multiple parts of your code access one piece of data without needing to copy that data into memory multiple times.

  • references are immutable like variable, write &mut foo instead of &foo to make it mutable.

  • we could written this code as:

    io::stdin().read_line(&mut guess).expect("Failed to read line");
    

    but this is more readable:

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");
    
  • read_line() pass whatever user enters into it, also returns a value, this case io::Result.

  • Result types are enumerations(also enums, with a fixed set of possibility known as variants)

  • Result’s variants are Ok or Err

    • Ok indicates the operation was successful, abd inside Ok is the generated value
    • Err means the operation failed, and inside Err contains the error log
  • io::Result has an expect method you can call. if this instance of io::Result is an Err, expect will cause the program to crash and display the message to passed as an argument to expect

  • without expect, program will still compile, but you will get a warning

  • the right way to suppress the warning is to actually write error handling, but in our case we will just crash the program when a program occurs.

  • crate is a collection of Rust source code files. the project we’ve been building is a binary crate, which is an executable.

  • the rand crate is a library crate, which contains code intended to be used in other programs, and can’t be executed on its own.

  • change Cargo.toml to add rand crate as a dependency

  • Cargo understands *Sematic Versioning(SemVer, MAJOR.MINOR.PATCH, MAJOR when making incompatible API changes, MINOR when adding functionality in backwards compatible manner, PATCH when make backwards compatible bug fixes), the number 0.8.4 is actually shorthand for ^0.8.4, which mean any version that is at least 0.8.4, but below 0.9.0, Cargo considers these version to have public APIs compatible with version 0.8.4.

  • then run cargo build to rebuild the project with added dependencies.

  • Rust creates the Cargo.lock file the first time when you run cargo build, Cargo will figures out all the versions of the dependencies that fit the criteria then save to it. When you build your project again in the future, cargo will see that Cargo.lock exists, and will use these specifed version rather than figuring out versions again.

  • This lets you have a reproducible build automatically, so crate rand will remain at 0.8.4 until you explicitly upgrade it

  • to upgdate a crate, run cargo update

  • Ordering is an enum, it has the variants Less, Greater and Equal.

  • match expression is made up of arms, an arm consists of pattern to natcg against, and the code that should be run if value given to match fits that arm’s pattern

  • i32, a 32-bit number; u32, an unsigned 32-bit number; i64, a 64-bit number

  • trim method on a String instance will eliminate any whitespace at the beginning at end

  • parse method on strings parses a string into some kind of number, we need to tell Rust the exact number type we want by using let guess : u32. parse method might failed if charaters can’t logically converted into numbers, and it will returns a Result type, like the read_line method

  • loop keyword creates an infinite loop

  • break keyword exit the loop

  • Ok(num) => num just return the num that parse produced and put inside the Ok value.

  • _ in Err(_) is a catchall value, in this case to match all Err values.

  • continue keyword tells the program to go to the next iteration of the loop.


Complete Code:

use std::io; // import I/O modules from Standard library
use std::cmp::Ordering;
use rand::Rng;

fn main()
{
    println!("---Guess The Number---");

    let secret = rand::thread_rng().gen_range(1..101);
    println!("The secret number is: {}", secret);
    
    loop //infinite loop
    {
        println!("Please input your guess:");

        let mut guess = String::new(); // Create a mutable variable that is currently bound to a new, empty instance of a String
        
        io::stdin() // Calling "stdin" function from "io" module
            .read_line(&mut guess)
            .expect("Failed to read line");

        let guess : u32 = match guess.trim().parse() // Shadow the previous value of guess with this new one.
        {
            Ok(num) => num, // just return the num that parse produced
            Err(_) => continue,
        };

        println!("Your guess: {}", guess); // The "{}" set of curly brackets is a placeholder, holdes the first value listed after the format string

        match guess.cmp(&secret)
        {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => 
            {
                println!("You win!");
                break;
            }
        }
    }
}