Rust - Concept of Smart Pointers
Last Updated :
21 Jul, 2021
Pointers are basically variables that store the address of another variable. While smart pointers are data types that behave like a traditional pointer with additional capabilities such as automatic memory management or bounds checking.
What makes smart pointers different from normal pointers are:-
- They are structs and contain additional metadata and capabilities.
- They do not borrow the data rather they own it.
What makes smart pointers different from structs:-
- They implement the Deref and Drop traits.
Deref trait allows an instance of a smart pointer struct to behave like a reference so that the code that works with pointers can also work with smart pointers.
Drop trait allows us to customize the code that should run when an instance of the smart pointer goes out of scope.
Some of the smart pointers are:-
- Box<T> to allocate values on the heap
- Rc<T> a reference counting type that enables multiple ownership
Box<T>
Box allows us to store data in the heap contrary to the default scheme of Rust to store as a stack.
Box is basically used:
- For dynamic allocation of memory for variables.
- When there is a lot of data that we need to transfer ownership, and we don't want them to are copied.
Let's create a box to store i32 value in a heap
Rust
fn main() {
let num = Box::new(4);
println!("num = {}", num);
}
Output
num = 4
Using Box<T> for recursive type
We will be using cons list to create a list of values. cons list takes to values the first one is the current value and the other is the next value, it performs a recursive call to cons function to generate a list, where the base condition for the recursive call is Nil.
Rust
enum List {
Cons(i32, List),
Nil,
}
use crate::List::{Cons, Nil};
fn main() {
let list = Cons(1, Cons(2, Cons(3, Nil)));
}
It won't compile because the List variable size cannot be determined prior to compilation.
Output:
rustc -o main main.rs
error[E0072]: recursive type `List` has infinite size
--> main.rs:1:1
|
1 | enum List {
| ^^^^^^^^^ recursive type has infinite size
2 | Cons(i32, List),
| ---- recursive without indirection
|
= help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `List` representable
error[E0391]: cycle detected when processing `List`
--> main.rs:1:1
|
1 | enum List {
| ^^^^^^^^^
|
= note: ...which again requires processing `List`, completing the cycle
= note: cycle used when computing dropck types for `Canonical { max_universe: U0, variables: [], value: ParamEnvAnd { param_env: ParamEnv { caller_bounds: [], reveal: UserFacing, def_id: None }, value: List } }`
There is an error that tells the List has infinite size because the compiler cannot determine the size of List during compilation. So we will be using a pointer to the list rather than the list itself to overcome this error. Since the size of the pointer is fixed irrespective of the data type to which it is pointing to, therefore the compiler can determine its size during compilation. Let's see this implementation using Box<T>
Rust
#[derive(Debug)]
enum List {
Cons(i32, Box<List>),
Nil,
}
use crate::List::{Cons, Nil};
fn main() {
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
println!("{:?}",list)
}
Output:
Cons(1, Cons(2, Cons(3, Nil)))
Defining our own smart pointer:
Rust
struct CustomPointer<T>(T);
impl<T> CustomPointer<T> {
fn new(x: T) -> CustomPointer<T> {
CustomPointer(x)
}
}
Here, we have defined a Custom smart pointer as a tuple struct with one value. We have defined a new method which is called when the smart pointer is instantiated, that returns a custom smart pointer.
Using Dereference trait on our custom smart pointer:
Just like an ordinary pointer, we can get the value of the box by using the '*' operator.
Rust
struct CustomPointer<T>(T);
impl<T> CustomPointer<T> {
fn new(x: T) -> CustomPointer<T> {
CustomPointer(x)
}
}
//implementing deref trait
use std::ops::Deref;
impl<T> Deref for CustomPointer<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn main() {
let x = 5;
let y = CustomPointer::new(x);
println!("{}",x==5);
println!("{}",*y==5);
}
Output:
true
true
Using Drop trait on our smart pointer:
Whenever the instance of smart pointer goes out of scope then the drop function is called.
Rust
// defining custom smart pointer
struct CustomPointer <T>(T);
impl<T> CustomPointer<T> {
fn new(x: T) -> CustomPointer<T> {
CustomPointer(x)
}
}
use std::ops::Deref;
// for implementing deref trait
impl<T> Deref for CustomPointer<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
// for implementing drop trait
impl <T> Drop for CustomPointer<T> {
fn drop(&mut self) {
println!("Dropping CustomtPointer with data");
}
}
fn main() {
let x = 5;
let y = CustomPointer::new(x);
println!("{}",x==5);
println!("{}",*y==5);
}
Output:
true
true
Dropping CustomtPointer with data
Similar Reads
Rust - Concept of Ownership
Rust programming language is known for its memory management. This article explains how Rust uses its Ownership model for better memory management. If you want to have a recap of Rust syntax before getting on this article you can refer to the Rust Archives. Generally, there are 2 ways to manage memo
4 min read
Rust - Box<T> Smart Pointer
Box allows us to store data in the heap contrary to the default scheme of Rust to store as a stack. Box is basically used for: For dynamic allocation of memory for variables.When there is a lot of data that we need to transfer ownership and we don't want that they are copied. Let's create a box to s
2 min read
Rust - Reference Counted Smart Pointer
In Rust, there is a concept of a Smart pointer where we use multiple ownership explicitly using the Rc<T> type. This Rc <type> is referred to as Reference counting. This concept was introduced in Rust to handle scenarios where a single value of the variable has multiple owners. Rc<T
2 min read
Pointers to Structures in Objective C
A pointer is a variable whose value is the address of another variable, e.g., stores the address of the memory location. Like any variable or constant, you must declare a pointer before you can use it to store variable addresses. It simplifies the programs and reduces their length. It is useful for
3 min read
Rust lifetime Constructor
In Rust, we have a concept of a lifetime constructor that falls under the ownership category. Rust enforces a certain set of rules through its concept of ownership and lifetimes. In this article, we would be learning more about Lifetimes in Rust. In Rust, there are various regions that are present i
3 min read
Rust - From and Into Traits
In Rust, we have the From and Into Traits that are linked internally and are implemented in type, conversion scenarios say in scenarios where we convert from type A to type B. The syntax for From Trait: impl trait From<T>Â {Â Â fn from(T) -> Self;} From traits in Rust are mainly used for eva
3 min read
C++ Modify Pointers
Prerequisite: Pointer in C++ Pointers are an essential part of the C++ language. It allows us to store the memory address of a variable. One thing to note here is that if we change the value that a pointer is pointing to, then the value of the original variable also changes. Now, let's check the app
2 min read
Rust - Constants
Constants are the value that can not be changed after assigning them. If we created a constant, then there is no way of changing its value. The keyword for declaring constant is const. In Rust, constants must be explicitly typed. Â The below syntax is used to initialize a constant value: Syntax : con
1 min read
Rust - Concept of Data Freezing
Rust is a systems programming language. In Rust, there is a concept of freezing which states that whenever data is borrowed, the borrowed data is also frozen. This frozen data cannot be modified until the references get out of scope i.e. all the bindings go out of scope. Suppose in the given below e
1 min read
Application of Pointer in C++
In C++, pointer is a variable that stores the memory address as its value. It is a powerful feature that forms the backbone of lower-level memory manipulation in C++. It is used in a variety of applications. Some of the common applications of pointers are discussed in this article.1. Pass Arguments
4 min read