sushh.com

Sushmita (aka meowy)

Rust Notes

Welcome to my Rust learning journey! These are comprehensive notes I've taken while working through the Rust Book and exploring Rust concepts.

Table of Contents

  1. Chapter 1: Getting Started
  2. Chapter 2: Programming a Guessing Game

Chapter 1: Getting Started

Introduction to Rust

Rust is one of the most beloved programming language out there by developers. It's more about empowerment than just putting it into the category of yet another programming language to learn. We can talk about and I could probably write over five thousands of words why rust is an amazing programming language but that's not what we're here for. So let's get started.

I guess the very first step will always have to be the installation:

$ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
 

This commands starts a scripts and start installation of rustup which later installs the latest version of rust. If you wanna see the version, then you'll need to rusn $ rustc --version and if you wanna update rustup update to update.

Let's write some rust to get our hands on:

The most basic step of learning a programming language is to write "hello world"! Let's just do that:

fn main() {
    println!("Hello, world!");
}

The fn main () {} defines a function named main, and this is a special function because it runs first on every executable rust program. But it has no parameter so it returns nothing, if it had parameters it would have gone inside () these parenthesis. Then the function body is wrapped in {} these curly braces,and rust requires curly braces all around function bodies. The body of the main function holds println!("Hello, world!"); .

This little line does all the work to print the text "Hello, world!" to the screen. The println! calls a rust macro, if it had called a function instead then we would write println without the ! .

Rust macros are a powerful metaprogramming feature which lets you write code that generates other code at compile time. We will explore more on macros later.

To compile the rust code you can write rustc [main.rs](http://main.rs) , here main.rs is the file name.

Cargo?

Cargo is a built in system and package manager in rust. Cargo does a lot of tasks such as building your code, downloading necessary librariesa dn building those. So far we'v written simple rust program without any dependancies but if we had some dependancies then we would've used cargo. You can run cargo --version to see cargo version.

Let's create a new project with cargo by running:

cargo new hello_rust

we'll see something like this on the terminal:

 Creating binary (application) `hello_rust` package
note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

now lets go into the new direcory cd hello rust , the file structure would look something like this.

HELLO_RUST/
├── src/
│   └── (source files)
├── target/
│   └── (compiled binaries and build artifacts)
├── .gitignore
├── Cargo.lock
└── Cargo.toml

Now let's try to understand what's so different than th old hello world program we wrote before.

Let's build the project and check:

cargo build
   Compiling hello_rust v0.1.0 (/Rust_Projects/hello_rust)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.41s

This creates an executable file in target/debug/hello_rust rather than in your current directory. Because the default build is debug build and cargo puts the binary in the directory named debug. If you cargo run it compiles and execute and shows the result. It's simpler cause you dont need to remember the binary path to run the executable and get result.

cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/hello_rust`
Hello, world!

There's also another command cargo check which checks your code and make sure that it compiles but doesn't really produce any executables.


Chapter 2: Programming a Guessing Game

Guessing Game?

Let's program the simple guessing game from the Rust Book.

The very first version:

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}");
}

It just takes an input from user and processes that. Let's break it down step by step use std::io; this bring io from rust standard library, this is for input/outputs.

Then the another new concept let mut guess = String::new(); , now over here let creates a new variable named guess and mut makes the variable mutable or changeable, it's important because by default rust variables are immutable. String::new() creates a new, empty String object.

Now io::stdin() calls the stdin() funcation from io module which then returns a handle to the standard input stream like keyboard input.

.read_line() is a method that belongs to the standard input system and it reads a line of texts from the user mostly everything until user inputs Enter from their keyboard. Now & or this ampersand means we are creating a reference variable. Which means we are lending the mut guess variable to rust temporarily and this is called borrowing in rust. Because the function can use the variable but doesn't take ownership of it.

And over here .expect() handles potential errors if reading doesn't work.

But this one is too simple, let's make the guessing game a bit more challenging:

use std::cmp::Ordering;
use std::io;
 
use rand::Rng;
 
fn main() {
    println!("Guess the number!");
 
    let secret_number = rand::thread_rng().gen_range(1..=100);
 
    loop {
        println!("Please input your guess.");
 
        let mut guess = String::new();
 
        io::stdin()
            .read_line(&mut guess)
            .expect("Failed to read line");
 
        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,
        };
 
        println!("You guessed: {guess}");
 
        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        }
    }
}

use std::cmp::Ordering; brings the Ordering enum into scope so that we can use this for comparing numbers or the result of comparing two values.

And rand::Rng; provides methods to generate random numbers.

fn main() {
    println!("Guess the number!");
    let secret_number = rand::thread_rng().gen_range(1..=100);
 

Over here secret_number is immutable variable as we don't need to change it. And rand::thread_rng() creates a thread-local random number generator and .gen_range(1..=100); suggests the range which is 1-100.

loop {} continues until conditions are met or explicitly broken so this also allows multiple guesses.

        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,
        };
 

There's multiple fun things happening over here. We are creating a new variable called guess that's a different type u32 than the original string because we can't compare a string with the number and we need a number to compare the results. This is called shadowing.

Then with guess.trim().parse() , here guess.trim() removes the whitespace so like user typed "42\n" but with this it will be "42" and .parse converts it to a number.

Then we have match which just handles both of the outcomes Ok(num) => num if parsing worked, use the number and Err(_) => continue - if parsing failed, restart the loop.

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

Over here cmp() method compares two numbers and returns an Ordering enum. It handles three possible comparison outcomes. Ordering::Less when guess is smaller than the secret number, then there's also greater one and equal so when it matches and becomes equal then it breaks the loop.

Finally we just finished our guessing game.