Everyone is rewriting their C++ code into Rust. Why? No, It's Not Performance
in systems languages like c++ , c etc you manage the memory yourself ( Heap, Stack, etc) and there is no Garbage Collector like in Languages Like JavaScript and Golang
but managing memory yourself has one big issue : it can cause a lot of bugs because it’s very hard to write safe memory code.
and these bugs are not detectable by the compiler. so you get them on Runtime.
Rust Solves this, Rust gives you these Errors on Compile Time.
The Performance of Rust and C++ are Same.
in today’s article we will be going over various ways in which you can write buggy c++ code and how to prevent that
There are majorly Three Categories of memory bugs
use-after-free
memory-leak
double-free
Use-After-Free
Basically, when you reference a pointer after calling free() on it, it no longer points to a valid memory location, so de-referencing it causes errors.
Memory Leaks
you typically allocate memory using a pointer inside a local scope (usually inside functions), or indirectly by creating an object that contains a pointer as a member. When the scope ends, the pointer itself (being a stack variable) is destroyed, but the memory it was pointing to is not automatically freed. If you don’t call free() or delete before the scope ends, the allocated memory remains reserved, but you have no way to access it anymore — this is a memory leak.
Double free
it’s basically a special case of use-after-free
A double-free bug occurs when you call free() (or delete) on the same pointer more than once. This typically happens when multiple functions assume they “own” the pointer and each one tries to free it.
there are some other interesting ways through which you can run into these memory errors
Rule of Three
if a class needs a user-defined destructor then it also needs a user-defined copy constructor and vice versa
Iterator Invalidation
An iterator is an object that acts like a pointer to an element within a container. For a std::vector, an iterator might be implemented as a simple raw pointer to an element in the vector’s internal, contiguous array.
Certain modifications to a container can force it to reallocate its internal storage. The most common example is adding an element to a std::vector that has reached its capacity.
The vector allocates a new, larger block of memory on the heap.
It copies (or moves) all existing elements from the old memory block to the new one.
It deallocates the old memory block.
At this point, any iterators that were pointing to elements in the old memory block are now invalidated. They have become dangling pointers. Dereferencing them is a use-after-free, resulting in Undefined Behavior.
In a single-threaded context, this is a common but often manageable bug. In a multi-threaded context, it becomes a nightmare. If one thread is iterating over a shared vector while another thread concurrently adds an element to it, the first thread’s iterator can be invalidated mid-loop, leading to crashes or silent data corruption that are extremely difficult to reproduce and debug.
c++ has tried to come up with solutions to these problems with smart pointers
two types of smart pointers
unique pointers
shared pointers
Unique Pointers
when you define a unique pointer, the scope in which you define it, when the pointer goes out of scope, then the delete is automatically called on its memory so the issue of memory leakage is partially solved ( i will explain why partially ahead )
now if you want to pass that pointer to a function, there are two options
Transfer the ownership
the origional pointer becomes null, and the new scope (the function) is responsible for deleting the memory to the new pointer and it is automatically deleted once the scope ends
Borrowing
Delete is not called
it still doesn’t solve the problem completely. suppose while borrowing the pointer you call delete on it accidentally then you will get into the use-after-free error
Shared Pointers
unique pointers are great but what if you want to pass ownership between multiple functions? can’t do it via them
so we introduce shared_pointers
shared_pointer point to a memory block which is a struct with two members 1. pointer to actual data 2. a counter
when you initialize this shared pointer the counter is 1
this shared_pointer is only destroyed when the counter goes to zero
when you pass this shared_ptr to any function the counter gets incrimented and when the pointer goes out of the scope the counter decrements
but shared_ptr has a major drawback of Cyclic Dependencies
this can be solved using another type of smart pointers called free_ptr but it’s not very elegent
A weak_ptr is created from a shared_ptr.
It “observes” the object but does not contribute to the reference count. It does not own the object.
Because it doesn’t own the object, the object can be deleted out from under it. Therefore, you cannot dereference a weak_ptr directly.
To access the object, you must call the .lock() method on the weak_ptr. This method checks if the object still exists. If it does, .lock() returns a new shared_ptr to it (atomically incrementing the ref count), which you can then safely use. If the object has already been deleted, .lock() returns a null shared_ptr
that’s it for this blog, now I will be going to learn rust.
hope another blog on rust coming out soon








