# Introduction to Rust

By Bobby Iliev, 2023-03-24. https://bobbyiliev.com/post/introduction-to-rust/

## What is Rust?

Rust is a systems programming language. It is designed to be fast, reliable, and maintainable.

Rust was started in 2006 by Graydon Hoare at Mozilla Research. It was originally designed to be a safer alternative to C++. It has since become a popular language for writing low-level code, such as operating systems and device drivers. Version 1.0 was released in 2015.

Firefox started sponsoring Rust and Firefox Quantum was rewritten in Rust in 2017, which boosted Firefox's performance.

## Installing Rust

To install Rust, you can use [rustup](https://rustup.rs/). Rustup is the official tool for installing Rust. It will install the Rust compiler and Cargo, Rust's package manager, and build tool.

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

## What is Cargo?

Cargo is Rust's package manager and build tool. It is used to build, test, and package Rust projects.

If you are coming from a JavaScript background, Cargo is similar to npm. Or if you are coming from a Python background, Cargo is similar to pip.

The compiler that Cargo uses is called `rustc`. You can use `rustc` to compile a single file.

## Hello World

To create your first Rust project, you can use Cargo's `new` command. This will create a new project with the name `hello_world`.

```
bash cargo new hello_world
```

Output:

```
     Created binary (application) `hello_world` package
```

This will create a new directory called `hello_world` with the following structure:

```bash
hello_world
├── Cargo.toml
└── src
   └── main.rs
```

The `Cargo.toml` file contains the project's metadata and is the configuration file for your project. Rust uses semantic versioning, so you can specify the version of Rust that your project requires.

The `src` directory contains the source code for the project. 

The `main.rs` file is the entry point for the project.

To run the project, you can use the `run` command which also builds the project.

```
bash cargo run
```

Output:

```
   Compiling
   hello_world v0.1.0 (hello_world)
   Finished dev [unoptimized + debuginfo] target(s) in 0.82s
   Running `target/debug/hello_world`
   Hello, world!
```

The `target` directory contains the compiled project. The `debug` directory contains the unoptimized version of the project.

You can add `--release` to the `run` command to build the project in release mode.

```bash
cargo run --release
```

The `release` directory contains the optimized version of the project.

## Variables

To declare a variable, you can use the `let` keyword. The `let` keyword is used to create a variable that is immutable by default. This means that the variable cannot be changed once it is created this improves the safety and the performance.

```rs
let x = 5;
```

Rust is a strongly typed language, so you must specify the type of the variable. The type of the variable is inferred from the value that is assigned to the variable.

```rs
let x: i32 = 5;
```

You can initialize multiple variables on the same line.

```rs
let (x, y) = (1, 2);
```

To initialize a mutable variable, you can use the `mut` keyword.

```rs
let mut x = 5;
```

As in almost all programming languages, you can declare constants with the `const` keyword.

```rs
const MAX_POINTS: u32 = 100_000;
```

The `const` keyword is used to create a constant that is valid for the entire time a program runs. Constants are always immutable. The convention for constants is to use all uppercase with underscores between words.

### Scope

Variables are scoped to the block that they are declared in. This means that variables declared in a block are only accessible in that block.

```rs
fn main() {
    let x = 1;
    let y = 10;
    println!("x = {}, y = {}", x, y);
}
```

The `y` variable is only accessible in the block that it is declared in. If you try to access the `y` variable outside of the block, you will get a compiler error.

```
error[E0425]: cannot find value `y` in this scope  --> src/main.rs:6:35
| 6 |     println!("x = {}, y = {}", x, y);     |
                    ^ not found in this scope
```

### Shadowing

You can declare a new variable with the same name as a previous variable. This is called shadowing. The new variable shadows the previous variable. The previous variable is no longer accessible.

```rs
fn main() {
    let x = 1;
    let x = x + 1;
    println!("x = {}", x);
}
```

The `x` variable is shadowed by the `x` variable on the second line.

The first `x` variable is no longer accessible.

## Memory Safety

Rust is a memory safe language. This means that Rust prevents memory errors such as null pointer dereference, use after free, and double free.

Uninitialized memory is a common source of memory errors. Rust prevents uninitialized memory by requiring you to initialize all variables.

```rs
fn main() {
    let x: i32;
    println!("x = {}", x);
}
```

If you try to use an uninitialized variable, you will get a compiler error.

```
error[E0381]: use of possibly uninitialized variable: `x`  --> src/main.rs:3:25
| 3 |     println!("x = {}", x);     |
                    ^ use of possibly uninitialized `x`
```

## Functions

Functions are declared with the `fn` keyword.

The `fn` keyword is used to declare a function that is immutable by default. This means that the function cannot be changed once it is created.

```rs
fn hello_world() {
    println!("Hello, world!");
}
```

The Rust guidelines recommend using `snake_case` for function names.

To call a function, you can use the function name followed by parentheses.

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

Functions do not have to appear in the same order as they are called. You can call a function before it is declared.

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

### Parameters

Functions can have parameters. Parameters are declared in the function signature.

```rs
fn hello_world(name: &str) {
    println!("Hello, {}!", name);
}
fn main() {
    hello_world("World");
}
```

The `&str` type is a string slice. A string slice is a reference to a string.

String slices are immutable by default. This means that the string slice cannot be changed once it is created.

### Return Values

Functions can return values. The return type is declared in the function signature.

```rs
fn add(x: i32, y: i32) -> i32 {
    x + y
}
fn main() {
    let sum = add(1, 2);
    println!("sum = {}", sum);
}
```

The `-> i32` part of the function signature declares the return type of the function.
The `-> i32` part of the function signature is optional.

If the function does not return a value, you can omit the `-> i32` part of the function signature.

## Modules System

Rust uses a module system to organize code. Modules are declared with the `mod` keyword.

```rs
mod hello_world {
    pub fn hello_world() {
        println!("Hello, world!");
    }
}
fn main() {
    hello_world::hello_world();
}
```

The `use` keyword is used to bring a module into scope.

```rs
mod hello_world {
    pub fn hello_world() {
        println!("Hello, world!");
    }
}
use hello_world::hello_world;
fn main() {
    hello_world();
}
```

## Scalar Types

There are 4 scalar types in Rust: integers, floating-point numbers, Booleans, and characters.

Types of integers:
- Signed integers: `i8`, `i16`, `i32`, `i64`, `i128`, `isize`
- Unsigned integers: `u8`, `u16`, `u32`, `u64`, `u128`, `usize`

The `isize` and `usize` types depend on the kind of computer that your program is running on: 64 bits if you’re on a 64-bit architecture and 32 bits if you’re on a 32-bit architecture.

The default integer type is `i32`. This is usually the fastest, even on 64-bit systems. The default integer type can be changed with the `#![feature(default_int_type)]` attribute.

```rs
#![feature(default_int_type)]
fn main() {
    let x = 1;
    println!("x = {}", x);
}
```

Floating-point numbers:
- `f32`
- `f64`

The default floating-point type is `f64`. This type is usually the fastest, even on 32-bit systems. The default floating-point type can be changed with the `#![feature(default_float_type)]` attribute.

```rs
#![feature(default_float_type)]
fn main() {
    let x = 1.0;
    println!("x = {}", x);
}
```

Booleans:
- `true`
- `false`

Characters are always 4 bytes in size and represent a Unicode Scalar Value. Literal characters are specified with single quotes.

Strings do not use the character type. Strings are a collection of characters.

## Compound Types

There are 2 compound types in Rust: tuples and arrays.

### Tuples

Tuples are a collection of values of different types. Tuples are declared with parentheses.

```rs
fn main() {
    let x = (1, 2.0, "three");
    println!("x = {:?}", x);
}
```

The `{:?}` format specifier is used to print the tuple.

You can use a dot to access a tuple element.

```rs
fn main() {
    let x = (1, 2.0, "three");
    println!("x.0 = {}", x.0);
    println!("x.1 = {}", x.1);
    println!("x.2 = {}", x.2);
}
```

### Arrays

Arrays are a collection of values of the same type. Arrays are declared with square brackets.

```rs
fn main() {
    let x = [1, 2, 3];
    println!("x = {:?}", x);
}
```

The `{:?}` format specifier is used to print the array.

Arrays are limited to a size of 32 elements. If you need a collection of more than 32 elements, you should use a vector.

You can use a dot to access an array element.

```rs
fn main() {
    let x = [1, 2, 3];
    println!("x[0] = {}", x[0]);
    println!("x[1] = {}", x[1]);
    println!("x[2] = {}", x[2]);
}
```

## Control Flow

### `if` Expressions

The `if` keyword is used to create an `if` expression.

```rs
fn main() {
    let x = 1;
    if x == 1 {
        println!("x is 1");
    }
}
```

### `if`-`else` Expressions

The `else` keyword is used to create an `if`-`else` expression.

```rs
fn main() {
    let x = 1;
    if x == 1 {
        println!("x is 1");
    } else {
        println!("x is not 1");
    }
}
```

### `if`-`else if`-`else` Expressions

The `else if` keyword is used to create an `if`-`else if`-`else` expression.

```rs
fn main() {
    let x = 1;
    if x == 1 {
        println!("x is 1");
    } else if x == 2 {
        println!("x is 2");
    } else {
        println!("x is not 1 or 2");
    }
}
```

You can assign the result of an `if` expression to a variable.

```rs
fn main() {
    let x = 1;
    let y = if x == 1 {
        2
    } else {
        3
    };
    println!("y = {}", y);
}
```

Note that there is a semicolon after the `if` expression when it is assigned to a variable.

We can't use `return` in an `if` expression.

There is no ternary operator in Rust. The `if` expression is the closest thing to a ternary operator.

```rs
fn main() {
    let x = 1;
    let y = if x == 1 { 2 } else { 3 };
    println!("y = {}", y);
}
```

### `loop` Expressions

The `loop` keyword is used to create a `loop` expression.

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

The `break` keyword is used to break out of a `loop` expression.

```rs
fn main() {
    loop {
        println!("Hello, world!");
        break;
    }
}
```

To break a nested `loop` expression, you can use a label.

```rs
fn main() {
    'outer: loop {
        'inner: loop {
            println!("Hello, world!");
            break 'outer;
        }
    }
}
```

### `while` Expressions

The `while` keyword is used to create a `while` expression.

```rs
fn main() {
    let mut x = 1;
    while x <= 3 {
        println!("x = {}", x);
        x += 1;
    }
}
```

There is no `do-while` loop in Rust. But you can use a `loop` expression with a `break` expression.

```rs
fn main() {
    let mut x = 1;
    loop {
        println!("x = {}", x);
        x += 1;
        if x > 3 {
            break;
        }
    }
}
```

### `for` Expressions

The `for` keyword is used to create a `for` expression.

```rs
fn main() {
    for x in 1..4 {
        println!("x = {}", x);
    }
}
```

The `iter` method is used to iterate over a collection.

```rs
fn main() {
    let x = [1, 2, 3];
    for y in x.iter() {
        println!("y = {}", y);
    }
}
```

The `enumerate` method is used to iterate over a collection and get the index of each element.

```rs
fn main() {
    let x = [1, 2, 3];
    for (i, y) in x.iter().enumerate() {
        println!("i = {}, y = {}", i, y);
    }
}
```

The `for` can use a range.

```rs
fn main() {
    for x in 1..4 {
        println!("x = {}", x);
    }
}
```

The `for` can use a range with a step.

```rs
fn main() {
    for x in (1..4).step_by(2) {
        println!("x = {}", x);
    }
}
```

## Strings

There are at least 6 different string types in Rust. But we will focus on the most common string types.

### String slices

String slices are a reference to a string. String slices are declared with double quotes.

```rs
fn main() {
    let x = "Hello, world!";
    println!("x = {}", x);
}
```

A literal string always a borrowed string slice.

```rs
fn main() {
    let x = "Hello, world!";
    let y = "Hello, world!";
    println!("x = {}", x);
    println!("y = {}", y);
    println!("x == y: {}", x == y);
}
```

### `String` type

The `String` type is a heap-allocated string. The `String` type is declared with the `String::new` function.

```rs
fn main() {
    let x = String::new();
    println!("x = {}", x);
}
```

The `String` type is declared with the `to_string` method.

```rs
fn main() {
    let x = "Hello, world!".to_string();
    println!("x = {}", x);
}
```

The `String` type is declared with the `String::from` function.

```rs
fn main() {
    let x = String::from("Hello, world!");
    println!("x = {}", x);
}
```

A borrowed string slice can be converted to a `String` type.

```rs
fn main() {
    let x = "Hello, world!".to_string();
    println!("x = {}", x);
}
```

### `&str` type

The `&str` type is a borrowed string slice. The `&str` type is declared with the `&` operator.

```rs
fn main() {
    let x = "Hello, world!";
    println!("x = {}", x);
}
```

A `String` type can be converted to a borrowed string slice.

```rs
fn main() {
    let x = "Hello, world!".to_string();
    let y = &x;
    println!("x = {}", x);
    println!("y = {}", y);
}
```

A vector of `String` types example.

```rs
fn main() {
    let x = vec!["Hello, world!".to_string(), "Hello, world!".to_string()];
    println!("x = {:?}", x);
}
```

## Ownership

Ownership is a Rust feature that allows you to manage memory. The Rust compiler ensures that memory is always valid.

Each value in Rust has a variable that's called its owner. There can only be one owner at a time. When the owner goes out of scope, the value will be dropped.

There is only one owner of a value. The value is moved to the new owner.

When the owner goes out of scope, the value will be dropped.

```rs
fn main() {
    let x = 1;
    let y = x;
    println!("x = {}", x); // error here
    println!("y = {}", y); // value moved here
}
```

### Stack and Heap

Stack:
- Fast access
- Fixed size
- Last in, first out
- In order

Heap:
- Slow access
- Dynamic size
- First in, first out
- Out of order

### Clone

The `clone` method is used to clone a value.

```rs
fn main() {
    let x = 1;
    let y = x.clone();
    println!("x = {}", x);
    println!("y = {}", y);
}
```

### References and Borrowing

References allow you to refer to some value without taking ownership of it.

```rs
fn main() {
    let x = 1;
    let y = &x;
    println!("x = {}", x);
    println!("y = {}", y);
}
```

The `&` operator is used to create a reference.

Under the hood, the `&` operator is a `borrow` method.

References are immutable by default. The `&mut` operator is used to create a mutable reference.

```rs
fn main() {
    let mut x = 1;
    let y = &mut x;
    *y += 1;
    println!("x = {}", x);
    println!("y = {}", y);
}
```

To dereference a reference, use the `*` operator.

```rs
fn main() {
    let mut x = 1;
    let y = &mut x;
    *y += 1;
    println!("x = {}", x);
    println!("y = {}", y);
}
```

### Structs

A struct is a custom data type that you can name and shape however you want.

```rs
struct Point {
    x: i32,
    y: i32,
}

Note that there is a comma after the last field.

Instanciating a struct.

```rs
fn main() {
    let x = Point { x: 1, y: 2 };
    println!("x = {:?}", x);
}
```

An associated function is a function that is associated with a struct.

```rs
impl Point {
    fn new(x: i32, y: i32) -> Self {
        Self { x, y }
    }
}

fn main() {
    let x = Point::new(1, 2);
    println!("x = {:?}", x);
}
```

The `Self` type is an alias for the type that the `impl` block is for.

### Methods

Methods are functions that are associated with a struct.

```rs
impl Point {
    fn new(x: i32, y: i32) -> Self {
        Self { x, y }
    }

    fn distance(&self) -> f64 {
        ((self.x.pow(2) + self.y.pow(2)) as f64).sqrt()
    }
}

fn main() {
    let x = Point::new(1, 2);
    println!("x = {:?}", x);
    println!("x.distance() = {}", x.distance());
}
```

### Traits

Traits are similar to interfaces in other languages.

```rs
trait HasArea {
    fn area(&self) -> f64;
}

impl HasArea for Point {
    fn area(&self) -> f64 {
        0.0
    }
}

fn main() {
    let x = Point::new(1, 2);
    println!("x = {:?}", x);
    println!("x.area() = {}", x.area());
}
```

Rundown of the code above:

- The `HasArea` trait is defined.
- The `HasArea` trait is implemented for the `Point` struct.
- The `area` method is defined.
- The `area` method is implemented for the `Point` struct.


A hands on example of traits.

```rs
trait Car {
    fn new() -> Self;
    fn drive(&self);
}

struct Toyota {
    model: String,
}

impl Car for Toyota {
    fn new() -> Self {
        Self {
            model: "Corolla".to_string(),
        }
    }

    fn drive(&self) {
        println!("The {} is driving.", self.model);
    }
}

fn main() {
    let x = Toyota::new();
    x.drive();
}
```

### Copy

The `Copy` trait is used to indicate that a type can be copied.

```rs
#[derive(Copy, Clone)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let x = Point { x: 1, y: 2 };
    let y = x;
    println!("x = {:?}", x);
    println!("y = {:?}", y);
}
```

### Debug

The `Debug` trait is used to indicate that a type can be printed.

```rs
#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let x = Point { x: 1, y: 2 };
    println!("x = {:?}", x);
}
```


## Collections

Collections are data structures that can group multiple values into a single type.

### Vectors

Vectors are resizable arrays. They can grow or shrink in size.

```rs
fn main() {
    let mut x = vec![1, 2, 3];
    x.push(4);
    println!("x = {:?}", x);
}
```

The `push` and `pop` methods are used to add and remove elements from the end of a vector.

```rs
fn main() {
    let mut x = vec![1, 2, 3];
    x.push(4);
    x.pop();
    println!("x = {:?}", x);
}
```

The `!vec` macro is used to create a vector.

### Hash Maps

Hash maps are key-value stores. In some languages, they are also known as dictionaries or associative arrays.

```rs
fn main() {
    let mut x = HashMap::new();
    x.insert("one", 1);
    x.insert("two", 2);
    println!("x = {:?}", x);
}
```

Common methods on hash maps:
- `insert`: Insert a key-value pair into a hash map.
- `get`: Get the value associated with a key.
- `remove`: Remove a key-value pair from a hash map.
- `len`: Get the number of key-value pairs in a hash map.

```rs
fn main() {
    let mut x = HashMap::new();
    x.insert("one", 1);
    x.insert("two", 2);
    println!("x = {:?}", x);
    println!("x.get(\"one\") = {:?}", x.get("one"));
    x.remove("one");
    println!("x = {:?}", x);
    println!("x.len() = {:?}", x.len());
}
```

## Enums

Enums are types that have a few definite values.

```rs
enum Color {
    Red,
    Green,
    Blue,
}

fn main() {
    let x = Color::Red;
    println!("x = {:?}", x);
}
```

Enums can also have data associated with them.

```rs
enum Color {
    Red,
    Green,
    Blue,
    RgbColor(u8, u8, u8),
    CmykColor { cyan: u8, magenta: u8, yellow: u8, black: u8 },
}

fn main() {
    let x = Color::RgbColor(0, 0, 0);
    println!("x = {:?}", x);
}
```

### Option

The `Option` enum is used to indicate that a value may or may not be present.

```rs
fn main() {
    let x = Some(1);
    println!("x = {:?}", x);
}
```

The `match` expression is used to handle the different cases of an enum.

```rs
fn main() {
    let x = Some(1);
    match x {
        Some(i) => println!("x = {:?}", i),
        None => println!("x is empty"),
    }
}
```

To create a `None` value, use the `None` keyword.

```rs
fn main() {
    let x: Option<i32> = None;
    println!("x = {:?}", x);
}
```

### Result

The `Result` enum is used to indicate that a function may or may not succeed.

```rs
#[must_use]
enum Result<T, E> {
    Ok(T),
    Err(E),
}
```

The `Result` enum has two variants: `Ok` and `Err`. The `Ok` variant indicates that the function succeeded. The `Err` variant indicates that the function failed.

```rs
use std::fs::File;

fn main() {
    let x = File::open("hello.txt");
    println!("x = {:?}", x);
}
```

You can `unwrap` a `Result` to get the value inside the `Ok` variant.

```rs
use std::fs::File;

fn main() {
    let x = File::open("hello.txt").unwrap();
    println!("x = {:?}", x);
}
```

You can also `expect` a `Result` to get the value inside the `Ok` variant.

```rs
use std::fs::File;

fn main() {
    let x = File::open("hello.txt").expect("Failed to open hello.txt");
    println!("x = {:?}", x);
}
```

## Closures

A closure is a function that can capture its environment.

The type and the arguments of a closure are defined in the same way as a function.

```rs
fn main() {
    let x = |a: i32, b: i32| -> i32 { a + b };
    println!("x(1, 2) = {}", x(1, 2));
}
```

A closure will borrow a reference:

```rs
fn main() {
    let x = 1;
    let y = |a: i32| -> i32 { a + x };
    println!("y(2) = {}", y(2));
}
```

Here is an example on how to use a closure to sort a vector.

```rs
fn main() {
    let mut x = vec![1, 3, 2];
    x.sort_by(|a, b| a.cmp(b));
    println!("x = {:?}", x);
}
```

Rundown of the code above:
- We create a vector with the values `1`, `3`, and `2`.
- We sort the vector using the `sort_by` method.
- The `sort_by` method takes a closure as an argument.
- The closure takes two arguments: `a` and `b`.
- The closure returns a `std::cmp::Ordering` value.
- The `cmp` method compares two values and returns an `Ordering` value.
- The `sort_by` method sorts the vector based on the `Ordering` value returned by the closure.

## Threads

Threads are used to run multiple tasks simultaneously.

```rs
use std::thread;

fn main() {
    thread::spawn(|| {
        println!("Hello from a thread!");
    });
    println!("Hello from the main thread!");
}
```

The `thread::spawn` function takes a closure as an argument and returns a `JoinHandle`. A `JoinHandle` is a handle to a thread that can be used to wait for the thread to finish.

The more threads you have, the more context-switching you have to do. This can slow down your program if you have too many threads.

## Conclusion

This is the end of the Rust tutorial. I hope you enjoyed it. If you have any questions, feel free to ask them in the [community forum](/questions).

As the next steps, I recommend you to:

- Read the [Rust book](https://doc.rust-lang.org/book/).
- Read the [Rust reference](https://doc.rust-lang.org/reference/).
- Read the [Rust by example](https://doc.rust-lang.org/rust-by-example/).
- Read the [Rustonomicon](https://doc.rust-lang.org/nomicon/).
