For the fifth consecutive year, Rust has been ranked as the most loved programming language in a survey of developers conducted by Stack Overflow. There are various reasons why developers love Rust, in one of my first articles of 2022, Genesis in Rust I mentioned that one of the most attractive features of Rust is its Safety.
Rust uses a system of ownership and borrowing to manage memory at compile time, rather than relying on garbage collection or manual memory management. Ownership refers to a set of rules that determine which part of a program is responsible for managing a particular piece of memory, and when that memory can be freed. In this article we will dive into Ownership and later on Borrowing in the next article.
In Rust, each value has a single owner at any given time. When a value is assigned to a variable or passed as an argument to a function, the ownership of that value is transferred to the new variable or function parameter. When the owner goes out of scope (i.e., when the variable is no longer in use), Rust automatically frees the memory associated with that value by inserting a drop statement.
This approach ensures that memory is always properly managed and avoids common issues such as null pointer errors or memory leaks. Additionally, Rust’s borrowing system allows for safe and efficient sharing of memory between different parts of a program, without the need for costly runtime checks.
The Stack and the Heap
The stack and the heap are two different regions of memory that are used to store values in different ways.
The stack is a region of memory used for storing data that has a known size at compile time and a relatively short lifetime. Examples of data stored on the stack include function arguments, local variables, and function return values. The stack operates in a last-in-first-out (LIFO) manner, meaning that the last item added to the stack is the first one to be removed.
The heap, on the other hand, is a region of memory used to store data that needs to persist beyond the lifetime of a single function call. Memory allocated on the heap is not automatically managed by the Rust compiler and must be manually managed using pointers. Heap memory is allocated and freed using Rust’s Box type, which provides a way to allocate memory on the heap and automatically free it when the Box is dropped.
Rust provides several different types of pointers that can be used to interact with heap-allocated data, including references, boxes, and smart pointers such as Rc and Arc. Rust’s ownership model ensures that heap-allocated data is always properly managed, with memory being freed automatically when it is no longer needed. This approach helps to prevent many common memory-related bugs, such as use-after-free errors, buffer overflows, and null pointer dereferences.
Overall, the stack and the heap are two distinct regions of memory in Rust that are used to store data in different ways as mentioned , with the stack being used for short-lived data of a fixed size, and the heap being used for dynamically allocated data of varying size and longer lifetime.
How ownership works
The Rust compiler enforces ownership rules at compile time, checking that all values have a valid owner and that there are no ownership violations such as multiple owners of a single value. This helps to ensure that Rust code is free from common memory management errors, making it safer and more reliable than other programming languages.
It is crucial to understand the three basic rules of ownership, which determine how memory is stored in the stack and heap.
- Each Rust value has a variable called its owner :
let x = 67; // x is the owner of the value "67"
Ownership can be transferred between variables via moves, which transfer ownership of the value to the new variable and invalidate the original variable.
When the owner goes out of the scope, the value it owns is dropped (i.e., memory is freed) :
In our introduction, we established a fact that ownership isn’t like the garbage collector system and, in fact, Rust doesn’t deal with a garbage collector system. Most programming languages either use a garbage collector or require the developer to allocate and free up memory themselves.
Ownership and functions
When values are passed to a function, they must follow the same ownership rules as other values. Meaning they can only have one owner at a time, and free up memory once out of scope.
The second rule of ownership, which states that a value can only have one owner at a time, results in verbose function writing. This is due to the need to transfer ownership of functions through return statements whenever you want to use them, as illustrated in the example above.
It is very memory-inefficient to make copies of data whenever passing to a function. The best method is to use the Rust references feature and borrow the data.
Conclusion
Rust’s ownership feature is a critical aspect of the language that can greatly impact a developer’s ability to create scalable code. The more comfortable a Rust programmer becomes with ownership, the easier it becomes for him or her to write scalable and predictable code, which in turn can help to avoid unexpected errors or bugs. Many developers appreciate Rust specifically because of its strong ownership model, which helps prevent unexpected behaviour.
In this article, we’ve explored the fundamentals of ownership in Rust, including its rules and best practices for incorporating it into our programs. Additionally, we’ve discussed certain Rust features that don’t rely on ownership, and how to use them effectively. For additional resources on Rust ownership, be sure to consult the official documentation to deepen your understanding and enhance your coding skills.
☞ Follow me on Twitter & Linkedin
☞ Kindly subscribe for my upcoming articles
Top comments (0)