Play with Dynamic Memory using Smart Pointers in Rust

Reading Time: 4 minutes

Smart Pointers are the data structures that behaves like a pointer while providing additional features such as memory management. Smart Pointers keep track of the memory that it points to, and is also used to manage other resources such as File handling and network connections.

We have mainly five Smart Pointers in rust from which we will discuss two smart pointers in this blog and rest three will discuss in next blog.

The concept of Smart pointers is not the strange for Rust. They are originated in many other languages like C++. We have different Smart Pointers in Rust which is already defined in the standard library provide functionality beyond that provided by references. In this blog, we will discuss some different examples on smart pointers like drop trait, by which we can customise the behaviour of drop function which enables when any instance goes out of scope and we will also enables you to have multiple owners of data by keeping track of the number of owners by the reference counter smart pointer.

The Rust can give some strong concepts like ownership and borrowing on which we can differentiate the references and smart pointers, the references can only borrow the data but the smart pointer can own the data that it points to.

Rust allocates everything on the stack by default. The Smart Pointers are the way in Rust to save memory in heap. We have already studied few smart pointers, such as String and Vec<T>, although we didn’t call them Smart Pointers at the time. Both these types count as smart pointers because they own some memory and allow you to manipulate it.
Usually Structs is use to implement the Smart Pointers.

Types of Smart Pointers

This image has an empty alt attribute; its file name is types.png
Figure:1 Types of Smart Pointer.

1. Box<T>:

  • This is a smart pointer that points to the data which is allocated on the heap of type T. It allow you to store the data on the heap rather than the stack.
  • Box<T> is an owned pointer.
  • It do not have a performance overhead, other than storing the data on the heap.
  • The destructor automatically called and destroy the objects and release the memory, when the Box goes out of scope.

For example:

fn main()  
{  
  let a = Box::new(5);  
  print!("Value in Box pointer is {}", a);
}   

In this example, a contains the value of Box that points to the data 5. If we access the value of Box, then the program prints ‘5’.

Cons List:

  • Cons keyword stands for Construct Function.
  • It is a data structure, which is use to construct new list.
  • It can take two argument, data type and the “List”.

For Example:

use List::{Cons, Nil};  

#[derive(Debug)]   
enum List {  
    Cons(i32, Box<List>),  
    Nil,  
}  

fn main()  
{  
  let recursive_list = Cons(1,Box::new(
                                       Cons(2,Box::new(Nil))));  
    
    print!("{:?}", recursive_list);  
}  

In this Example, we have use Box<T> for recursive type data in which we have made a recursive list by the use of Cons List, then we put our data in Box<T> smart pointer recursively.

This image has an empty alt attribute; its file name is download-1.png
Figure: 2 Recursive List Representation.

2. Deref<T>:

  • It is used to customize the behavior of deference operator (*).
  • When we implement the Deref<T> trait, then the smart pointer will be treated as a reference. Then, the code that works on the references will also be used on the smart pointers too.

Implementation of a Deref Trait:

  • The deref method can be implement by the predefined Deref trait in the standard library.
  • The deref method borrows the self and returns a reference to the inner data.

For Example:

use std::ops::Deref;  

#[derive(Debug)]   
 struct MyBox<T>  
{  
  a : T,  
}  
impl<T> Deref for MyBox<T>  
{  
  type Target = T;  
  fn deref(&self) ->&T  
  {  
    &self.a  
  }  
}  

fn main()  
{  
  let instance = MyBox{a : 10};  
  print!("{}",*(instance.deref()));  
}  

In this example:

  • We have used Box smart pointer to implement the Deref trait.
  • In deref trait we implements the deref() method, and this method will returns the reference of ‘a’ variable.
  • The “type Target = T” is an associated type for a Deref trait.
  • Then we have created the instance of MyBox type, instance.
  • Then the deref() method is called by using the instance of MyBox type, and then reference which is returned from deref() method is dereferenced.

NOTE:

This is the first part of the series of blogs on Smart Pointers, I’ll post next blog soon. Stay Tuned.

Thanks for Reading!!!

This image has an empty alt attribute; its file name is blog-footer.jpg
Knoldus Pune Careers - Hiring Freshers

Get a head start on your career at Knoldus. Join us!