How To Use Ownership In Rust?

Introduction to Rust

Rust is a modern system programming language that aims to provide safe and efficient code. Graydon Hoare created it while he was working at Mozilla, and later on, it was acquired by Mozilla. It is designed to be a low language system programming with a high abstraction. The syntax of Rust is similar to that of C++. Rust has a lot of unique features like ownership and borrowing. In this article, we will learn about ownership and borrowing in Rust.

Scope of Variable

The scope is the range within a program for which any variable is valid. For example, consider a variable s defined inside a pair of curly braces.

{
  let s="Rust"; // scope of s starts from here
  //operations on 's' can be performed here 
} //scope of s ends here

In the above example, the scope of 's' starts from where it is defined and ends when the curly braces end. In between these, we can perform any operations on s as we want, but once the scope of 's' ends, we can not perform any operations on 's' as it is invalid now.

Ownership In Rust

Some languages allow the programmer to manage the memory explicitly, while others use garbage collection. The ownership system in Rust is a set of rules that governs the memory managed at runtime. In Rust, every value has a variable that owns it, and this ownership can be transferred to another variable. This ensures memory safety and data races, which ensures the safe management of memory.

Rules Of Ownership

Some rules must be followed to implement the concept of ownership. They are as follows

  • Each value has an owner. For example, let x=10; 10 is the value, and x is its owner.
  • There can only be one owner at a time. For example, let x=10; Here, only x is the owner of 10.
  • When the owner goes out of scope, the value will be dropped.

For example,

fn main()
{
  let x=10;
}

In the above example, when x goes out of scope, the value 10 also goes out of scope.

Example of Ownership

fn main()
{
  let s1 = String::from("Rust");
  let s2=s1;
  println!("s1:{s1}"); // This line will give error as s1 is no longer valid
  println!("s1:{s2}");
}

In the above example, we have created a string variable named s1 and assigned a value to it. In the next line, we have assigned s1 to s2, another variable. The moment we assigned s1 to s2, s1 becomes invalid, and the value can only be accessed from s2. When we try to print the variable s1, it will give an error as it is no longer valid, and its value is now assigned to the variable s2.,

Here, the ownership of s1 is transferred to s2. So, the new owner is s2, and the old owner, s1, is no longer valid.

Use of Copy trait 

In Rust, copy trait is an annotation used on integer data type, which is stored on the stack memory. If copy trait is used, the old variable from which the value is copied can be used further. This can be used on the following data type.

  • All types of integers.
  • All types of Floating points.
  • Character.
  • Boolean, true, or false.

For example

fn main()
{
  let x:i64=10;
  let y=x;
  println!("Value of x is:{}",x);
  println!("Value of y is:{}",y);
}

In the above example, we created a variable x of integer type and assigned the value 10. In the next line, we have created another variable, y, and assigned the value of x in y. And after that, we printed both the values of x and y. When we pass the ownership of x to y, it is not moved like String type but got copied using the copy trait. Hence, even after y=x; line, we can still access the value of x

Copy trait is useful, but it can be used with only limited data types, not all.

Taking ownership back

So far, we have learned how to transfer ownership. Now let's look at how to take back ownership with the example below.

fn main() {
    let s1 = gives_ownership();  // gives_ownership moves its return value into s1
    let s2 = String::from("Rust");  // s2 comes into scope
    let s3 = takes_and_gives_back(s2); // s2 is moved into this function, which moves its return value into s3
} // Here, s3 goes out of scope. s2 was moved and s1 goes out of scope.

fn gives_ownership() -> String { // gives_ownership will move its return value to the function that calls it.
   let some = String::from("yours"); //some comes into scope
   some       //some is returned and assigned to the calling function.
}

fn takes_and_gives_back(a: String) -> String { // a comes into scope
    a  // a is returned and moves out to the calling function
}

In the above example, we have created a variable named s1 which will contain the value returned from the function named gives_ownership(). In the function gives_ownership(), we have created a string variable named some and assigned it value. In the next line, we have returned that variable to the calling function of gives_ownership(). Here we pass the ownership of variable some to the calling function, which assigns it to the variable s1. 

In the next line, we have created a string variable named s2 and assigned it a string. Further, we have created another variable named s3, and we have called a function named takes_and_gives_back() in which we have passed the variable s2, which is of string type. In the takes_and_gives_back() function, a string is passed as an argument, i.e, the same as a=s2. Here, the ownership of s2 is passed to variable a. In the next line, we return a to the calling function, which gets assigned to the variable named s3. Here, we are returning the ownership to the main function back.

So, using the above method, we can transfer one value's ownership to another and take it back.

Conclusion

Ownership is a powerful concept introduced in Rust which makes Rust a very safe language that efficiently manages memory. Using ownership, we need not worry about memory management, as values assigned to the variable automatically get dropped when the variable gets out of the scope. Also, if the programmer tries to access the variable after giving its ownership to another variable, the compiler won't allow it and will throw errors. Hence, it stops programmers from making such mistakes.

Now, here comes the concept of borrowing. The concept of ownership is useful, but in most cases, we do not want to transfer the ownership permanently; instead, we can let the other variable borrow it. In this article, we have learned about the concepts of ownership, and in the next article, we will learn about the concept of borrowing.


Similar Articles