Before Rust, there where only two strategies for languages to handle memory management. But now we do have three.
Let the developer handle the memory allocation & deallocation
Aka manual, and historically this was the first approach. C is a great example of this. With its two procedures malloc and free, the developper can calculate the memory he needs, manually allocate it, and then free it when no more needed. Giving the developper this capability does not come with a free cost, in large application with complex codebase, anyone could :
- Allocate bad size for the needed data
- Forget to free memory when it should be
- Free memory before it has to be, introducing another type of problems named dangling pointers, and thus accessing bad memory leading to security problems.
BUT, giving the developer the power manually handling memory can sometimes be perfect in many cases where high performance is a key, like realtime applications because we eliminate a considerable runtime execution time for GC algorithms to automatically free unused memory (GC comes with pauses …).
An automatic Memory management
The idea behind is to let the developer create the logic and let another system handling memory allocation and deallocation. This rises the safety of such programs, no more : pointer manipulations, manual memory allocation, dangling pointers, accessing bad memory addresses … We can though build systems that are less error prone, and developers can finally focus on the core features and not technical pointer arithmetic.
This system is called the Garbage collector that came to solve problems above. But quickly bring with it various performance degradations, VM pauses for Java. Then we had many implementations that gradually improves performance over time. shenandoah in Java 17 is the best example of a performant GC.
The Rust mode : Hybrid
And then rust came to propose another approach : Can we have memory automatically managed but at the same time avoid having a watcher that adds overhead to our runtime ? Yes, the rust compiler. At compile time, it calculates where to put the free memory, named drop function, and puit it where it should be in the source code, all just before compiling. This is done thanks to the Borrow checker. Variables are managed by the context that owns it, and overtime, variable’s ownership can be transferred or borrowed from context to another, the last owner of a varible is in charge of freeing its memory once this owner is no more in scope.
Rust solves yet another problem, dangling pointers with using slices, concurrency by setting all variable to a default immutable state. I will cover Rust’s concepts in another article. I highly recommand you to learn about Rust, I think more languages will be out of this idea, since this language confirmed its resilience by slowly invading linux kernel over C.