07 Jul, 2025
So I have been working with garbage collected programming languages mostly. I have written decent amount of C++ but that was during my uni days when I was solving LeetCode problems and college assignments. Recently I started learning Rust and here is my understanding of what’s happening beneath the abstractions.
You can see this as a blog where I guide you through what’s memory and how memory is allocated in Rust. This is what I understood so if you think I’ve written something wrong or can be improved please dm me on X/Twitter.
Ok so consider memory as a massive array where each element can hold 8 bits of data. This data is nothing but an address.
So stack works like you can only add/push or remove/pop from the top. It is extremely fast because the CPU just needs to move a pointer up or down.
When we create a variable in Rust (like below) the all go to the stack memory:
And here is how the memory looks like:
Some things to note here:
- Each variable is stored top to down as we declare them but the stack grows downwards so the point is pushed last but appears at the top in memory
- The rust compiler optimizes layout for performance so the actual in-memory layout might include gaps between variables due to padding
- Regarding the stack allocation:
- Size must be known at compile time
- Stack gets cleaned up when the variable goes out of scope
Heap is a dynamic memory region where you can request space at runtime. Instead of storing data directly you get a pointer to the allocated memory.
So here notice one thing, that the stack has a pointer “ptr” that points to the actual data on the heap.
Lets take an example:
Before the let y=x; here is how the memory layout would look like:
After let y = x; what happens is x is moved to y so:
Why is x invalid!?!?
Rust compiler invalidates x to prevent:
- Double-free: Both x and y trying to free the same memory
- Use-after-free: Using x after y has freed the memory
Lets take one more example (here y will borrow x):
What are references?
- They don’t own the data
- They won’t outlive the data they refer to
Box is a smart pointer that lets you put a value on the heap explicitly
Some types implement the Copy trait and are duplicated instead of moving them
Types that implement Copy (there might be more so crosscheck plz):
- integer types
- floating point types
- bool
- char
- Tuples of Copy types
- Arrays of Copy types
You noticed one thing!? Only those data types that are allocated on the Stack implement Copy trait. This is what I noticed, might be true/false.
But Rust is memory safe right!? Yes, Rust prevents user after free and double-free but we can still create reference cycles with Rc<T> and RefCell<T>
In the above code, it creates a cycle that won’t be freed.When two Rc pointers reference each other in a cycle, their reference counts never reach zero which means the memory is never freed. Why is that!? Cause Rc uses reference counting and to break cycles it uses Weak<T> for uni directional relationship which does not prevent deallocation when the strong reference is dropped.
- Stack: Fast, fixed size, automatic cleanup, Last In First Out (LIFO)
- Heap: Flexible size, requires allocation/deallocation, slower
- Ownership: Each value has exactly one owner
- Moving: Transfers ownership, prevents double-free
- Borrowing: Temporary access without giving the ownership
.png)
