This article was done using my notes from:
Lyubomir Gavadinov (2020), Fundamentals of the Rust Programming Language
Let’s go through the fundamentals of Memory Management. We look at the Stack, the Heap, Pointers, and Smart Pointers. These are all general concepts that are necesary for writing performant low level languages, such as Rust.
The Stack
The stack is a special region of the process memory that stores variables by each function. The memory for each function is called a stack frame. This is where local variables live.
For every function call a new stack frame is allocated on top of the current one. Only the function which created the stack frame has access to it. This is what gives scope to the function.
The size of every variable on the stack has to be known at compile time.
When a function exists its stack frame is released. We don’t have to worry about deallocating the memory, it is managed for us.
fn main() {
let a = 2;
let result = stack_only(a);
}
fn stack_only(b: i32) {
let c = 3;
}
It is important to remember that that the stack has a limited size. It is determined by the machine architecture, operating system, comiler and other factors. If we reach the end of the stack with an infinite recursion for example. Our program will crash with a stack overflow error.
The Heap
The heap is a region of the process memory that is NOT automatically managed for us. We have manually allocate memory there and also free this memory once done. Failing to release allocated memory will result in in memory leaks.
The heap has no size restrictions. It is only limited by the physical resources of the system.
It is accessible by any function, anywhere in the program.
Heap allocations are expensive and we should avoid them when possible.
FUNCTION main {
INTEGER a = 2
CALL stack_only(a)
}
FUNCTION stack_only(INTEGER b) {
NTEGER c = 3
CALL stack_and_heap
}
FUNCTION stack_and_heap {
INTEGER d = 5
POINTER e = ALLOCATE INTEGER 7
DEALLOCATE e
}
Smart Pointers
A pointer is an object that stores a memory address on the heap. Obtaining the value stored at the location is known as dereferencing the pointer.
A smart pointer is just a wrapper around the raw pointer giving it additional capabilities. The most common capability is to free memory when the pointer goes out of scope.
fn main() {
let a = 2;
let result = stack_only(a);
dbg!(result);
}
fn stack_only(b: i32) {
let c = 3;
stack_and_heap();
}
fn stack_and_heap() {
let d = 5;
let e = Box::new(7);
// When the smart pointer goes out of scope,
// it will make sure that the associated heap memory is freed.
}