Rust Learning

Based on the book heavily

Cargo Command Explanation

Let’s recap what we’ve learned so far about Cargo:

  • We can create a project using cargo new.

  • We can build a project using cargo build.

  • We can build and run a project in one step using cargo run.

  • We can build a project without producing a binary to check for errors using cargo check.

  • Instead of saving the result of the build in the same directory as our code, Cargo stores it in the target/debug directory.

use std::io;

fn main() {
    println!("Guess the number!");

    println!("Please input your guess.");

    let mut guess = String::new();

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

    println!("You guessed: {}", guess);
}

Since this code contains a lot of info to de-structure it.

use std::io;

By default,By default rust has a set of items defined in the standard library that it brings into the scope of every program. This is called prelude, by adding the statement std::io; library we add a number of useful features, including the ability to accept user input.

fn main() {

}

The fn main syntax is the main entry to all rust programs, the fn syntax declares a new function; the parentheses, (), indicate that there are no parameters; and curly bracket. {, starts the body of the function.

    println!("Guess the number!");

    println!("Please input your guess.");

The println! macro prints the text on the user screen.

Storing Values with Variables

    let mut guess = Strong::new();

This line creates a new variable named guess, another simpler example for let syntax is

let apples = 5; //immutable
let mut bananas = 5; //mutable

This code creates 2 Values, apples and bananas. the value known as apples is immutable meaning that it will always equal 5. While the value known as bananas is mutable meaning it can be changed. Just to expand on this:

let apples = 5; 
let apples = 6; 
// This would fail since apples is not mutable
let mut bananans = 5;
let bananas = 6; 
// If you used println! to check the vaule of bananas, it would be 6.     

Returning to the guessing game program, the let mut guess variable will introduce a mutable variable named guess. The equal sign (=) tells rust we want to bind something to the variable now. We are binding String::new, String is a string type provided by the standard library. The :: syntax in the ::new line indicates that new is an associated function of the String type. An associated function is a function that's implemented on a type, in this case String. This new function creates a new, empty string. You'll find a new function on many types because it's a common name for a function that makes a new value of some kind. In full, the let mut guess = String::new(); line has created a mutable variable that is currently bound to a new, empty instance of a String.

Receiving User Input

Recall that we included the input/output functionality from the standard library with these std::io; on the first line for the program. Now we'll call the stdin function from the io module, which will allow us to handle user input:

    io::stdin()
        .read_line(&mut guess)

If we hadn't imported the io library with use std::io; at the beginning of the program, we could still use the function by writing this function call as std::io::stdin . The stdin function returns an instance of std::io::stdin, which is a type that represents a handle to the standard input for your terminal.

Next, the line .read_line(&mut guess) calls the read_line method on the standard input handle to get input from the user. We're also passing &mut guess as the argument to read_line to tell it what string to store the user input in. The full job of read_line is to take whatever the user types into standard input and append that into a string (without overwriting its contents), so we therefore pass that string as an argument. The string argument needs to be mutable, so the method can change the string's content.

The & indicates that this argument is a reference, which gives 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 a complex feature, and one of rusts major advantages is how safe and easy it is to use references. Like variables, references are immutable by default. Hence, you need to write &mut guess rather than &guess to make it mutable.

Handling Potential Failure with Result

Going over the third line of text

    .expect("Failed to read");

It could have also been written as such:

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

The longer line is quite hard to read and understand, it's best practice to try and divide it and break it up when using the .method_name() syntax.

As mentioned earlier, read_line puts whatever the user enters into the string we pass to it, but it also returns a Result value. Result is an enumeration, often called an enum, which is a type that can be in one of multiple possible states. We call each possible state a variant.

Result's variants are Ok and Err. The Ok variant indicates the operation was successful, and inside Ok is the successfully generated value. The Err variant means the operation failed, and Err contains information about how or why the operation failed.

Values of the Result type, like values of any type, have methods defined on them. An instance of Result has an expect method that you can call. If this instance of Result is an Err value, expect will cause the program to crash and display the message that you passed as an argument to expect. if the read_line method returns an Err, it would likely be the result of an error coming from the underlying operating system. If this instance of Result is an Ok value, expect will take the return value that Ok is holding and return just that value to you so you can use it. In this case, that value is the number of bytes in the user's input.

If you don't call expect, the program will compile, but you'll get a warning:

$ cargo build
   Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
warning: unused `Result` that must be used
  --> src/main.rs:10:5
   |
10 |     io::stdin().read_line(&mut guess);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: this `Result` may be an `Err` variant, which should be handled
   = note: `#[warn(unused_must_use)]` on by default
help: use `let _ = ...` to ignore the resulting value
   |
10 |     let _ = io::stdin().read_line(&mut guess);
   |     +++++++

warning: `guessing_game` (bin "guessing_game") generated 1 warning
    Finished dev [unoptimized + debuginfo] target(s) in 0.59s

Rust warns that you haven't used the Result value returned from read_line, indicating that the program hasn't handled a possible error.

The right way to suppress the warning is to actually write error-handling code, but in our case we just want to crush this program when a problem occurs, so we can use expect.

Printing Values with println! Placeholders

Aside from the closing curly bracket, there's only one more line to discuss in the code:

    println!("You guessed: {}", guess);

The line prints the string that now contains the user's input. The {} set of curly brackets is a placeholder: think of {} as little crab pincers that hold a value in place. When printing the value of a variable, the variable name can go inside the curly brackets. When printing the result of evaluating an expression, place empty curly brackets in the format string, then follow the format string with a comma separated list of expressions to print in each empty curly bracket placeholder in the same order. Printing a variable and the result of an expression in one call to println! would look like this:

let x = 5;
let y = 10;

println!("x = {x} y + 2 = {}", y + 2);

This code would print x = 5 and y + 2 = 12.

Testing the First Part

Run cargo run to test this part of the guessing game:

$ cargo run
   Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
    Finished dev [unoptimized + debuginfo] target(s) in 6.44s
     Running `target/debug/guessing_game`
Guess the number!
Please input your guess.
6
You guessed: 6

At this point, the first part of the game is done: we're getting input from the keyboard and then printing it.

Generating a Secret Number

Next, we need to generate a secret number that the user will try to guess. The secret number should be different every time, so the game is fun to play more than once. We'll use a random number between 1 and 100, so the game isn't too difficult. Rust doesn't yet include random number functionality in its standard library. However, the rust team does provide a rand crate with said functionality.

Last updated