How Rust memory management work to beginners.
Tags: rust, beginners, programming, learning
Published: 2023-09-14 18:25:28

Do you ever thought about **what happens** to your **RAM** when you **run a program**? And how the **ways you write code can interfere** with many other things in your system?

That article will help you to understand more about *management memory* and **HOW RUST WORKS THIS SUBJECT**. 

## Table of Contents
- [1. Stack and Heap](#1-stack-and-heap)
- [2. Borrow checker](#2-borrow-checker)
- [3. Errors prevention](#3-errors-prevention)
- [4. Conclusion](#4-conclusion)

## 1. Stack and Heap

Before you learn what rust do, you have to learn some concepts. In general we have two types of memory, that are called: **Stack and Heap**. Now let's introduce them.

### 1.1 Memory: Stack

Stack, as the name says, **works like a stack**, following the principle of **"Last In, First Out" (LIFO).**  Maybe it will be easier explain it in steps:

* Imagine a **stack of dishes**;
* The **first dish** you put in is the **last to be removed**;
* When a **function is called**, a block of memory is **'stacked'** on top of the stack;
* When the **function ends**, this block is **"unstacked"**, freeing this memory. 

Normally, the **values** that will be stored on the stack is known by the **compiler** (in the compiling time), because it knows how much memory is needed to store. This process occurs **automatically**, all values are deleted from memory. 

Below is an example of this:

```rust
fn main() {
    let number = 12; // at this moment the variable has created

    println!("{}", number); // 12 
} // When the owner (main function) goes out of scope, the value will be dropped

```

In Rust we can create some scopes just by using `{}`,  and this adds layer into our stack with a limited lifetime. After your goes out from that specific *scope*, the memory is cleared and you lost the information related. A good and simple example of it is: 

```rust
fn main() {
    {
        let number = 12;

        println!("{}", number); // 12
    }

    println!("{}", number); // Cannot find value `number` in this scope
}
```

### 1.2 Memory: Heap

In a few words: the **heap** memory is a free memory space to allocate data that may change. 

Imagine that you need to **store some variable** after starting your program, this variable doesn't have a fixed size known at compile time since it can have size variations or is a direct allocation in memory. 

If one of the possibilities above matches, we know that we have **Heap** memory instead **Stack**. Heap has a more **flexible memory** and a large space. Take a look: 

```rust
let number = Box::new(12); // alocate a integer in heap

let name = String::from("Canhassi"); // alocate a String in heap
```

In Rust we have two `types` of string, `&str` and `String`.  

* **&str**: has a fixed size based on the text written;
* **String**: has a size that can be increased, decreased, and deleted.

... and that's why `String` is stored on the Heap, and `&str` on the stack.

One way to free heap memory is: when a **variable that stores** something that is on the heap **leaves the scope** of the function (reaches the end of the function), it will be freed in the same way as the stack.

Okay, now we have a clear image on both memory types, but what the difference to manage memory between Stack and Heap? Let's see these diferences!
## 2. Borrow checker 

Borrow Checker is the **part of the Rust compiler** that **checks and ensures** that ownership, borrowing, and lifetime **rules are respected**.

I'm gonna be honest with you: I had a some issues to understanding how this works at beginning, and I see that is common among new Rustaceans . But don't worry, friend of mine. I'll teach you on the best way possible. But first, let's take a look the code below: 

```rust
fn main() {
    let name = String::from("Canhassi"); // Creating a string variable

    print_name(name); // calling the print_name function passing the variable

    println!("{}", name); // Error: name borrowed to print_name()
}

fn print_name(name: String) {
    println!("{}", name); // print "Canhassi"
}
```

If you run the compiler with that code you gonna see the following error:

```
borrow of moved value: name
```

When we call the `print_name` function, the `name` variable will be **moved to another scope** and that scope will be the **new owner** of it. And the rule of owner goes out scope will applied again. Borrow Checker ensures that **once ownership is transferred**, the original variable **can no longer be used** to access that value. this happened in the code above.

Another way that you can use the original variable is using references like that. 

```rust
fn main() {
    let name = String::from("Canhassi"); // Creating a string variable

    print_name(&name); // calling the print_name function passing the variable

    println!("{}", name); // print "Canhassi"
}

fn print_name(name: &String) {
    println!("{}", name); // print "Canhassi"
}
```

PS: These rules of **borrow checker work** just with **objects allocated in heap**.
## 3. Errors prevention

The Borrow Checker is fundamental to ensuring Rust's memory safety without the need for a [garbage collector](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)).

In other languages like C and C++, we (as developers) have to deallocate memory manually with some functions.  C++ is known for allowing memory management errors, including [memory leaks](https://en.wikipedia.org/wiki/Memory_leak). C++ itself does not cause memory leaks. Instead, **C++ provides a great deal of flexibility** and control to the programmer, and this **freedom can lead to errors** if not used correctly.

A famous error is **Undefined Behavior**, for example, imagine a function that return e reference of a variable like that

```rust
fn main() {
    let number = foo(); // calling foo function
}

fn foo() -> &i32 {
    let number = 12; // creating a var number
    
    &number // try return a reference of var number
}
```

This code doesn't work because the Rust compiler has restrict rules with memory management. We can't return a reference of var `number` because the scope of this function will go out, and the var `number` will disappear, so Rust doesn't let this happen. In C++ we can do that, and it allows the notorious memory leaks. 

I think that is kinda cool to know that the Rust compiler avoids this type of error... If this subject is new for you, I can tell that memory leaks can cost a lot of money and is truly hard to fix it, since is never only one memory leak.

Other example is, **Double free**, that occur when you try free twice the same object for example in C code.

```c
char* ptr = malloc(sizeof(char));

*ptr = 'a';
free(ptr);
free(ptr);
```

This subject is really extensive and there's many possibilities to generate errors like that when you're working in other languages. But, I'll let you do your own research and make sure to tell me more about on this post comments!

[Undefined Behavior](https://en.wikipedia.org/wiki/Undefined_behavior)
[Double Free](https://heap-exploitation.dhavalkapil.com/attacks/double_free)
## 4. Conclusion

I wrote this article with the aim of presenting in a more general way how memory management works with Rust. But I recommend for a more complete knowledge the chapter 4 of [The Rust Programming Language book](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html). There you will have more examples and more explanations of this content.

I really hope this article was helpful!! 🦀🦀

[My twitter](https://twitter.com/CanhassiDesu)
[Linkedln](https://www.linkedin.com/in/canhassi/)