As mentioned in previous article what are different ways, programming languages use to manage memory. As a programmer we know that heap is the memory section where most of the complexity and potential problems arise. In almost all programming languages stack and other memory section behave similarly but heap management differs significantly from language to language.
Rust take a very unique approach to manage the heap memory but before that let’s first look how allocation and deallocation are generally handled and then explore the rust model
Allocation
Internally, Rust uses system allocator to allocate the memory, just like new in C++, Go, Java and malloc, calloc in C.
In Rust
let obj:Box<String> = Box::new();
In C++
int* ptr = new int(42);
In C
int *ptr=(int*)malloc(sizeof(int)*5);
in JAVA
String name = new String("Hello");
In Go
mp:=make(map[string]string)Deallocation
In C and C++ programmer has to deallocate the memory, In Java and Go Garbage collector deallocates the memory from heap.
But Rust uses very unique approaches to deallocate the heap memory; when a variable’s scope ends it calls drop function and cleans up the heap memory. This is made possible by ownership which is a set of rules enforced at compile time. There’s no garbage collector in Rust; instead, Ownership, Borrowing, and Lifetimes ensure memory safety without runtime overhead. let discuss these concepts in detail.
Ownership
This is the concept which enforces that one value in memory will have only one owner at a time. As the owner go out of scope automatically free the memory and no need of garbage collection.
This guarantees:
No double frees
No use-after-free
Automatic cleanup
let owner1:String=String::from("Hello");
here owner1 is owner of string Hello
let owner2=owner1;
now owner2 is owner of the string Hello and using owner1 you can't perform any kind of operation like push etc.Borrowing
When you have a value in Rust, it’s owned by exactly one variable. If you want to let other parts of your code access it without moving ownership, you borrow it.
Borrowing is how Rust ensures:
No dangling pointers
No data races (even in multithreaded code)
Memory is freed exactly once
There are two types of borrowing
Mutable: Can modify the value but at a time only one mutable borrowing is possible.
let mut s = String::from("Maneesh"); let r1 = &mut s; // let r2 = &mut s; // you will get compilation error
Immutable: read only access to the memory
let s = String::from("Maneesh"); let a = &s; let b = &s; println!("{}, {}", a, b);
Lifetime
When reference is passed at that time compiler has to know that when the owner of referenced memory will be invalid or go out of the scope. Reference should not outlive that can cause dangling pointer that’s how rust ensure memory safety.
fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() > s2.len(){
s1
} else {
s2
}
}‘a is the life time expression, input and output must live at least as long as ‘a is live. Every reference has lifetime explicit or implicit, lifetime only needed when compiler can’t infer. It is also checked at compile time so no runtime overhead.

