Rust Guessing Game Example

Rust Guessing Game Example

Learn Rust using a guessing game example.

[lwptoc]

Example 1

This is a guessing game example written in Rust.

Step 1: Create Project

  1. Open your Rust IDE.
  2. In the menu go to File --> Create New Project.

Step 2: Add Dependencies

Go to your Cargo.toml and modify it as follows:


[package]
name = "guessing_game"
version = "0.1.0"
authors = ["Inanc Gumus <[email protected]>"]
edition = "2018"

# A crate is a collection of Rust source code files.

# - we can't generate random nums using stdlib.
# + let's include the rand crate as a dependency.
#   this way, we can generate random numbers.
[dependencies]
rand = "0.7.3"

Step 3: Write Code

Next create a file known as main.rs and add the following code:

// 💯 See: _naked.rs for the annotation-free version.

// We need to bring the input/output library into scope,
// So we can accept user input and print the result as output.
// std => Standard Library
use std::io;

// For the stdout().flush() below.
use std::io::Write;

// We want to use the Rng from the rand crate to generate
// Random numbers.
//
// To install this, first you need to go to your Cargo.toml
// file, and add it there, below the [dependencies] section.
use rand::Rng;

// To compare values we'd also need to use the Ordering enum.
//
// Ordering enum contains: Less, Equal, and Greater.
//
// --> ::* at the end returns every value in the Ordering.
//     So we can use them directly like Less, Equal, Greater.
//     Instead of: Ordering::Less, Ordering::Equal, etc.
use std::cmp::Ordering::*;

fn main() {
    // Let's introduce a new immutable variable named secret.
    // We're going to compare this one with the user input: guess.
    //
    // --> Variables are immutable by default.
    //     (Immutable == Unchangeable).
    //
    // --> thread_rng() returns us a random generator that
    //     will be seeded by the current operating system thread.
    // --> gen_range() is a method on the returned random
    //     generator. It generates a random number between 1 and 100.
    let secret = rand::thread_rng().gen_range(1, 101);

    // loops infinitely
    loop {
        print!("guess: ");

        // stdout gets flushed on newlines.
        // print! doesn't contain a newline.
        // So we need to flush stdout ourselves.
        io::stdout().flush().expect("🤬 cannot flush");

        // Let's create a new, empty, and mutable string variable.
        //
        // ==> mut makes the variable mutable.
        //
        // ==> String::new() creates and returns a new String instance.
        // --> The String type is a growable, UTF-8 encoded text.
        // --> :: indicates new is an associated fn of String.
        //     (aka a static method).
        //
        // ==> Type Inference
        // --> Rust automatically guesses the type of the guess variable.
        // --> Since String::new() returns a String, so guess is also
        //     a String.
        let mut guess = String::new();

        // Let's accept user input and put it into the guess variable.
        //
        // ==> io::stdin() calls the stdin fn from the io module.
        // --> Returns a std::io::Stdin instance.
        // --> Stdin represents a handle to the stdin for your terminal.
        io::stdin()
            // Let's read the user input into guess.
            //
            // read_line takes whatever the user types into stdin, and
            // place that into a string, so it takes a string as an
            // argument.
            //
            // --> read_line() calls the read_line method on the Stdin
            //     instance to get input from the user.
            // --> &mut guess passes the mutable variable to the
            //     read_line method. So the readline can update it.
            // --> & indicates a reference. This allows read_line
            //     to work with the same guess instance. Prevents
            //     copying on memory. Passes a pointer to it.
            .read_line(&mut guess)
            // Let's handle the error: Could we read the user input?
            //
            // ==> read_line returns an io::Result enum that can either
            //     be Ok, or Err. Ok, and Err are called the variants of
            //     the io::Result enum type.
            //
            //     Ok  --> User input read successfuly.
            //     Err --> Couldn't read the user input.
            //
            // ==> Here we call the expect method of the io::Result.
            //     --> The result of the read_line defines the
            //         behavior of the expect method.
            //     --> If the result is Err: expect() crashes the
            //         program, and prints the error message.
            //     --> If the result is Ok: expect() extracts the
            //         return value from the Ok.
            //
            // ==> If you don't use the expect, cargo check will
            //     give you a warning about that you should handle
            //     the error.
            .expect("🤬 cannot read input");

        // Let's convert the user input into an integer.
        //
        // Rust is a strongly statically-typed programming language.
        // --> So you can't compare the user input (a string)
        //     to the secret (an i32—the default 32-bit integer type).
        //
        // ==> Let me explain the code below:
        // --> guess below is a new guess variable with u32 type.
        // --> u32 == a 32-bit unsigned integer.
        // --> trim() removes the spaces around the previous guess
        //     string variable's value.
        // --> parse() returns the string value as an u32 value.
        // --> expect checks if the previous guess's value was an
        //     integer, if not, crashes the program.
        //
        // let guess: u32 = guess.trim().parse()
        //      .expect("Please type a number!");

        // Let's not crash the program, and gracefully handle the err.
        // == match is explained at the end of the program ==
        let guess: u32 = match guess.trim().parse() {
            // 1. if the input is a number, return it.
            Ok(n) => n,

            // 2. if it's not reloop.
            // --> _ is a catch-all value.
            //    Here, it catches every type of err.
            Err(_) => {
                println!("🤬 not a number");
                continue;
            }
        };

        // crab pincers == {} — holds a value in-place.
        // --> println injects values into a string using placeholders.
        // --> {} is a placeholder, and the value is the guess's
        //     value.
        println!("you say: {}, let's see...", guess);

        // Let's compare the user input with the secret number.
        //
        // ==> cmp() method can compare two values that can be compared.
        // --> & means that cmp takes the reference of the secret variable.
        // --> It returns a std::cmp::Ordering value.
        //
        // ==> match expression allows you to compare values.
        match guess.cmp(&secret) {
            Less => println!("🤨 bigger"),
            Greater => println!("🤨 smaller"),
            Equal => {
                println!("🥳 you rock!");
                break; // exit the loop
            }
        }
        println!();
    }
}

Next create a file known as _naked.rs and add the following code:

use rand::Rng;
use std::cmp::Ordering::*;
use std::io;
use std::io::Write;

fn main() {
    let secret = rand::thread_rng()
        .gen_range(1, 101);

    loop {
        print!("guess: ");
        io::stdout()
            .flush()
            .expect("🤬 cannot flush");

        let mut guess = String::new();
        io::stdin()
            .read_line(&mut guess)
            .expect("🤬 cannot read input");

        let guess: u32 = match guess.trim().parse() {
            Ok(n) => n,
            Err(_) => {
                println!("🤬 not a number");
                continue;
            }
        };
        println!("you say: {}, let's see...", guess);

        match guess.cmp(&secret) {
            Less => println!("🤨 bigger"),
            Greater => println!("🤨 smaller"),
            Equal => {
                println!("🥳 you rock!");
                break; // exit the loop
            }
        }
        println!();
    }
}

Run

Copy the code, build and run.

Reference

Here are the reference links:

Number Link
1. Download Example
2. Follow code author
3. Code: Apache 2.0 License

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *