What is Ownership
Understanding Ownership
The distinctive quality of the Rust programming language is ownership, which ensures memory safety without the need for pointers or garbage collectors.
What is Ownership?
Ownership is the state in which a block of code is the owner of a resource. The resource is contained in an object created by the code block. The resource is released and the object is destroyed when the control reaches the conclusion of the block.
Important points of Ownership:
- A variable’s “owner” can alter its owning value in accordance with mutability.
- It is possible to move ownership to another variable.
- In Rust, ownership is merely semantically shifted.
- The ownership concept concurrently ensures safety.
Rules of Ownership
- In Rust, each value has an owner, which is a variable that is related to it.
- One owner at a time is permitted.
- The value attached to it is lost when the owner moves outside of their area of responsibility.
Example of Ownership
In Rust, variables can interact with one another. Let’s examine an illustration:
Step 1:
Assigning the value of x to the variable y:
let x=10;
et y=x;
In the example above, x is bound to the number 10. Next, the variable y is given the value of x. In this instance, the value of x is transferred to the variable y rather than being copied. As a result, the variable x is deleted and ownership of x is passed to the variable y. Rust issues an error if we attempt to utilize the variable x more than once. Let’s use an example to better grasp this.
fn main()
let x=10;
let y=x;
println!("value of x :{}",x);
Example
Memory and Allocation
Data can be stored in heap or stack memory in Rust.
Stack memory: Information is always added to and withdrawn from stack memory in the opposite order. It operates on the “last in, first out” tenet, which states that the material that is added last is always deleted first. An ordered memory is called stack memory. Because of how it accesses the memory, it is faster than heap memory. Heap memory is utilized to store the content if the size of the data is uncertain at build time.
Heap memory: A well-organized memory is a heap memory. The operating system returns a pointer after locating an empty space in the heap memory. The term “allocating on the heap” refers to this procedure.
This diagram illustrates how the content is contained in the heap and the pointer is in the stack.
Example
fn main()
{
let v1=vec![1,2,3];
let v2=v1;
}
Step 1:
Vector v1 binds to 1, 2, and 3 in the program’s initial statement. A vector consists of three components: its length, capacity, and a pointer to the memory that points to the data that is stored there. As seen below, these components are kept in the stack, whilst the data is kept in the heap memory:
Step 2:
The vector v1 is assigned to the vector v2 in the second statement of the program. We duplicate the pointer, length, and capacity on the stack, but we leave the data in the heap memory alone. Now let’s examine the representation of memory:
This kind of representation, nevertheless, may cause issues. Both v1 and v2 will attempt to release the memory once they are outside of scope. This results in two times as much free memory, which contaminates the memory.
Step 3:
To protect memory, Rust evades the step 2 criterion. Rust believes that the v1 vector is no longer valid and does not replicate the memory that has been allocated. Since v1 exits the scope, there is no need to release its memory.
Use of Copy trait
An unique annotation known as the copy trait is applied to types, such as integers, that are kept on the stack. Even after the assignment operation, the older variable can still be used if the copy trait is applied to the types.
The following are a few examples of copy types:
- Every integer type, including u32.
- The type bool is Boolean and has two values: true and false.
- Every floating type, including f64.
- The type of character, char.
Ownership and functions
The ownership of a variable is transferred to the variable of a called function when a variable is supplied to it. Assigning a value to a variable and sending value have the same semantics.
Let’s understand this through an example:
fn main()
{
let s=String::from("javaTpoint");
take_ownership(s);
let ch='a';
moves_copy(ch);
println!("{}",ch);
}
fn take_ownership(str:String)
{
println!("{}",str);
}
fn moves_copy(c:char)
{
println!("{}",c);
}
Output:
javaTpoint
a
a
The ownership of the’s’ variable is handed to the variable’str’ using the take_ownership() function in the example above, where the string’s’ binds with the value “javaTpoint”. The ‘ch’ variable binds to the value ‘a,’ and the moves_copy() function transfers ownership of the ‘ch’ variable to the variable ‘c’. As the ‘ch’ variable has a “copy” attribute as its type, it can also be utilized in the future.
Returning value and scope
When a function returns values, ownership is also transferred. Now let’s examine this:
fn main()
{
let x= gives_ownership();
println!("value of x is {}",x);
}
fn gives_ownership()->u32
{
let y=100;
y
}
Output:
value of x is 100
The y variable’s ownership is transferred to the x variable in the example above when the gives_ownership() function returns the value of y, or 100.