Ownership is the unique feature that Rust programming language offers and provides the guarantee of memory safety without using garbage collector or pointers.
What is Ownership?
When a block of code owns a resource, it is known as ownership. The block of code creates an object that contains the resource. When the control reaches the end of the block, the object is destroyed, and the resource gets released.
Important points of Ownership:
Rules of Ownership
Example of Ownership
Multiple variables can interact with each other in Rust. Let's look at an example:
Assigning the value of x to the variable y:
In the above example, x binds to the value 10. Then, the value of x is assigned to the variable y. In this case, the copy of x is not created rather than the value of x is moved to the variable y. Therefore, ownership of x is transferred to the variable y, and the variable x is destroyed. If we try to reuse the variable x, then Rust throws an error. Let's understand this through an example.
Following is the output of the above example:
Memory and Allocation
In Rust, data can be stored either in stack or heap memory.
Stack memory: In stack memory, data is always placed in order and removed in the opposite order. It follows the principle "last in first out", i.e., the data which is inserted last is always removed first. Stack memory is an organized memory. It is faster than the heap memory because of the way it accesses the memory. If the size of the data is unknown at compile time, then heap memory is used for storing the content.
Heap memory: Heap memory is an organized memory. The operating system finds an empty space in the heap memory and returns a pointer. This process is known as "allocating on the heap".
This diagram shows that stack contains the pointer while heap contains the content.
Let's see a simple example of memory allocation.
In the first statement of the program, vector v1 binds with the values 1,2 and 3. A vector is made up of three parts, i.e., a pointer to the memory pointing to the data stored in memory, length, and capacity of the vector. These parts are stored in the stack while data is stored in the heap memory as shown given below:
In the second statement of program, v1 vector is assigned to the vector v2. The pointer, length, and capacity are copied on the stack, but we do not copy the data in the heap memory. Let's look at the memory representation:
However, this type of representation can create a problem. When both v1 and v2 goes out of the scope, then both will try to free the memory. This causes the double free memory, and this leads to the memory corruption.
Rust avoids the step 2 condition to ensure memory safety. Instead of copying the allocated memory, Rust considers that v1 vector is no longer valid. Therefore, it does not need to free the memory of v1 as v1 goes out of the scope.
Use of Copy trait
Copy trait is a special annotation which is placed on the types like integers that are stored on the stack. If copy trait is used on the types, then the older variable can be further used even after the assignment operation.
Here are some of the types that are copy:
Ownership and functions
When a variable is passed to the function, then the ownership moves to the variable of a called function. The semantics of passing value is equal to the assigning a value to a variable.
Let's understand this through an example:
javaTpoint a a
In the above example, string 's' binds with the value "javaTpoint" and the ownership of 's' variable is passed to the variable 'str' through a take_ownership() function. The 'ch' variable binds with a value 'a,' and the ownership of 'ch' variable is passed to the variable 'c' through a moves_copy() function. The 'ch' variable can also be used afterwards as the type of this variable is a "copy" trait.
Returning value and scope
Returning values from the function also transfer the ownership. Let's look at this:
value of x is 100
In the above example, gives_ownership() function returns the value of y, i.e., 100 and the ownership of y variable is transferred to the x variable.