Diving into Rust’s Ownership

Reading Time: 5 minutes

Rust is a powerful systems programming language that empowers developers to build reliable and efficient software by giving them control of low-level details such as memory usage. It is one of the most popular programming languages and StackOverflow’s most loved language for four years in a row.

It is a type-safe programming language like C and C++ but different in the way that rust is easy to write while providing memory safety and avoiding memory leaks. 

Different languages can have different approaches to memory management. Programming languages like Python and Ruby use garbage collectors which constantly look for unused memory while C and C++ use malloc for allocating memory, but this leads to programmers writing long codes for memory management and can lead to a lot of bugs. Rust however uses a different approach for memory management.

In this blog, we will go through the concept of ownership which is rust’s way of memory management and makes rust different and fun from other languages.

Before starting with ownership it is important to understand the Stack and Heap storage. 

Stack

It is a part of memory that is available for storing temporary variables, mainly used to store the static data i.e data whose size is already known to us. It is a LIFO data structure.

The advantage of using stack is that we do not have to manage the memory ourselves, the CPU does that for us. Data on the stack is stored in the known consecutive memory location, therefore accessing data stored on the stack is faster.

The disadvantage of the stack on the other hand is that all the data stored on the stack must have a known size. But what happens when we do not know the size of the data which is going to be stored in the variable? For eg: when a user gives a string input which can be of n size. What do we do then? That type of data is stored on the Heap.

Heap

It is also another part of memory which stores data of unknown size.

Heap is used for storing dynamic data. It is the data whose size cannot be calculated at compile time. 

Memory allocators are responsible for finding big enough space to store the data and then returning a pointer to the location of that data. Accessing data on the heap is slower in comparison to stack because we have to follow a pointer to get there. This means that we are the ones who are managing memory now and have the responsibility to allocate and free memory. 

Managing the heap memory can take some work as we have to make sure there are no memory leaks, duplicate data, or unused data on the heap. This is where Ownership makes our lives easy. 

What is Ownership?

Now we will see Rust’s approach for memory management. The concept of ownership which rust uses is what makes it a unique systems programming language. It is defined by a set of rules:

  • Each value in Rust has a variable that’s called its owner.
  • There can only be one owner at a time.
  • When the owner goes out of scope, the value will be dropped freeing the memory.

While these rules may seem very simple and straightforward, the concept is still very new and it may take programmers some time to get a hang of it. Let us now understand these rules with the help of some examples and explore Rust’s ownership features.

Owner

Now to understand our first rule we need to be clear about the term owner. An owner of a value is the variable in which that value is stored. In simple terms, the variable is the owner.

fn main() {

   let owner_variable = String::from(“hello”); 

   println!(“value stored inside owner is: {}”, owner_variable)

}

Here the string value is stored inside the owner_variable which is indeed the owner of this value.

Moving Ownership

The second rule states there can be only one owner at a time. This means that if another variable wants to use the data stored in the owner variable, then the ownership along with the data is also passed to the new variable. Let’s understand this by using an example.

fn main() {

   let orignal_owner = String::from(“hello”); //initial owner of the value

   let new_owner = orignal_owner;  //value moved here

   println!(“value stored in original owner is : {}”, orignal_owner );

}

In this example the owner of the value hello is original_owner. We created another variable new_owner which is equal to the original_owner. In doing so not only we have passed the value of the original to new, but also the ownership of the value as well. To check this we have inserted a print statement after the value has been passed to new_owner

By our understanding of this rule, the original_owner now does not have the value and ownership and this print statement will throw an error.

As we predicted, it did throw an error.

Clone

Now there may be times you would want to copy the data instead of taking ownership of the value. For this, you can use a method clone(). This method will copy the data of the heap instead of moving the data along with the ownership.

Copy

There are some data types whose size at compile time is known therefore they are entirely stored on the stack. To copy the data of these data types we do not need to use the clone() method. Also copying data from these types will not result in loss of ownership for the original owner. Some of those types are

  • u32
  • bool
  • f64
  • char

Ownership scope

Till now we have a good understanding of the first two rules of ownership, now moving on to the third rule which defines that when the owner gets out of scope, the value will be dropped. This is what makes ownership more unique. When an owner is defined, it is present inside a scope, where the owner can move value and pass ownership. Once the scope is over Rust calls a drop() function at the closing bracket which will drop all values and free up ownership.

Let’s see an example to make it more clear.

 fn main() {

   let orignal_owner = String::from(“hello”);  

   {

       let check_scope = “drop it”;

   }

   println!(“The value for {} will not print”, check_scope);

}

Here we have defined a variable check_scope inside a bracket. By our understanding, the value of the check_scope should not be accessible outside the scope because Rust will call drop() function where the bracket closes.

Run this code and check for the output. It should look like this:

As we can see the error the rust compiler gives us is that check_scope is not found in the current scope.

Ownership is a feature that makes rust different from other languages. It is Rust’s way of managing memory that makes it unique. In our future Rust blogs, we will try to dive deep into Rust’s memory management and look at how we can access values outside the scope and pass values inside functions without passing ownership.

Note: I hope our blogs help you to enhance your learning. I’ll post more blogs on Rust. Stay Tuned.

If you want to read more content like this?  Subscribe Rust Times Newsletter and receive insights and latest updates, bi-weekly, straight into your inbox. Subscribe Rust Times Newsletter: https://bit.ly/2Vdlld7.

Happy learning!!!

This image has an empty alt attribute; its file name is screenshot-from-2020-06-08-11-00-35.png

This image has an empty alt attribute; its file name is footer-2.jpg

Discover more from Knoldus Blogs

Subscribe now to keep reading and get access to the full archive.

Continue reading