Have you ever asked yourself why the concept of “passing by value” and “passing by reference” exist?
From what I’m learning in Rust by going through the Rust Book, this happens because the memory is divided in two sections: The heap and the stack.
These two words have been something that scared me everytime I hear them, because the C classes given at college were horrible, and made them seem like an awfully complicated topic.
It turns out they are not so scary, and they are actually quite interesting.
The stack is a memory space that follows the FILO rule. It’s like a pile of plates, and I think you get the comparison.
The allocation of the stack memory is done by the compiler. The allocation follows an order (FILO).
The stack holds the context of the current function. When the program enters a function, it creates it’s arguments and all it’s scope variables on the stack.
One thing to notice is that the stack is fast. But like everything in computer science, this speed comes at a trade off. The stack can only hold fixed, immutable, known size variables, because this predictabillity is what makes it fast. So generally integers, chars, booleans, pointers, are all allocated on the stack, because these values take a known space in memory.
To sumarize, when a function is called, the stack is populated, and when the function returns, the stack is freed, automatically by the compiler.
The heap is another memory space, it doesn’t follows the FIFO rule. Instead, the heap space is used in random order to allocate global values or large amounts of data that aren’t fixed sized.
Think about it like this: an integer has a fixed allocated memory size, but a string doesn’t. A string can take from 1 to 10.000 charecters. Arrays too. Objects too.
This may be a shitty comparison, but think about the heap as a database that will be managed directly by you and will live as long your program is running. So the point of having order is not interesting for the heap, because you want to be able to allocate and free memory space “assynchronously”. In comparison, the stack “follows the lines” of your program, so is interesting for the stack to be “synchronous”. Heap is what is used when you drop
new . A pointer is fixed, known sized value on the heap which points to a dynamic sized value on the stack.
Finally, Rust Ownership
While C deals with the heap memory space through the use of
free , leaving the task to the programmer to manage memory imperatively and explicitly, Rust has the concept of Ownership, which are implicit rules of how rust manages memory.
Each value has a variable as it’s owner, and there can’t be two owners of the same value at the same time.
What is happening on the above code is that rust is transfering the onwership of the “hello” string from the variable
s1 , and it will not compile, because on line 4, s, which had it’s ownership transfered, and is now invalid
This happens because at runtime, the value of
s will be deallocated when the main function ends.
s is a pointer, and the runtime will us it to clear the heap. If the runtime has two pointers pointing to the same heap space, a double free memory error can happen, causing possibly memory corruption.
So what rust does is: it knows which is the last valid pointer, and uses only this pointer to free up memory.
There is also a relationship between ownership an functions.
Basically, functions take ownership of a value when they are passed as parameter, and functions can also give back ownership by returning the same value at the end.
This is very interesting, and I plan to write more notes as I go through the book and play some more with this incredible language!