🚀
17. Smart Pointer
Languages

Smart Pointer

- A pointer is a variable that stores the memory address, which points to any other data in the memory.

Mar 202510 min read

Smart Pointer

  • A pointer is a variable that stores the memory address, which points to any other data in the memory.

  • Most common pointer is the reference (only simply borrow the value, they point to, i.e. doesn't have ownership of the value).

  • References doesn't have any special capabilities, i.e. they doesn't have overhead (unlike pointers).

  • Smart Pointers are data structures that act like pointers, but also have additional metadata and capabilities.

  • In many cases, smart pointers have ownership of the data they point to (unlike references, which simply borrow values).

  • Smart pointers are implemented using structs (they implement the deref & drop traits).

  • deref trait allows an instance of the smart pointer struct to behave like a reference, so that you can write code that works with either references or smart pointers.

  • drop trait allows you to customize the code that is run when an instance of the smart pointer goes out of scope.

Box Smart Pointer

fn main() {
    let b = Box::new(5);
 
    println!("b = {}", b);
}
  • Box is a smart pointer, it stores address of the heap memory, where the value 5 is stored.

  • Boxes doesn't have overhead, except storing data in the heap (but they have very limited capabilities)

Usage:

Boxes are useful when you have a type whose size can't be known at compile time and you want to use a value of that type in a context that requires an exact size.

Example:

enum List {
    Cons(i32, List),
    Nil,
}
 
use List::{ Cons, Nil };
 
fn main() {
    let list = Cons(1, Cons(2, Cons(3, Nil)));
}
🚫

recursive type List has infinite size

2 |     Cons(i32, List), 
  |               ---- recursive without indirection
  • Cons() constructs memory objects which hold two values or pointers to two values.

Fix:

enum List {
    Cons(i32, Box<List>),
    Nil,
}
 
use List::{ Cons, Nil };
 
fn main() {
    let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}
  • Box instead of calculating the size of the type at compile time, it stores the address of the heap memory, where the value is stored.

Deref Trait

  • deref trait allows an instance of the smart pointer struct to behave like a reference, so that you can write code that works with either references or smart pointers.
fn main() {
    let x = 5;
    let y = &x;
 
    assert_eq!(5, x);
    assert_eq!(5, y);
}
🚫

Error: no implementation for {integer} == &{integer}

fn main() {
    let x = 5;
    let y = &x;
 
    assert_eq!(5, x);
    assert_eq!(5, *y);
}
  • *y dereferences the reference y to get the value it points to.

Deref Trait with Smart Pointers:

fn main() {
    let x = 5;
    let y = Box::new(x);
 
    assert_eq!(5, x);
    assert_eq!(5, *y);
}
  • Box smart pointer implements the deref trait, which allows it to be dereferenced like a reference.

  • Here *y dereferences the Box smart pointer, to get the value from the address storing the duplicate value stored in heap.

Custom Smart Pointer

  • Implementing the Deref trait on your own data structures allows you to use all the methods provided by the standard library.
use std::ops::Deref;
 
struct MyBox<T>(T);
 
impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}
impl<T> Deref for MyBox<T> {
    type Target = T;
 
    fn deref(&self) -> &T {
        &self.0
    }
}
 
fn main() {
    let x = 5;
    let y = MyBox::new(x);
 
    assert_eq!(5, x);
    assert_eq!(5, *y);
}

Implicit Deref Coercions

  • Implicit Deref Coercion is a convenience that Rust performs on arguments to functions and methods which implement the Deref trait.

  • It is used to convert a reference of one type into a reference of another type.

fn main() {
    let m = MyBox::new(String::from("Rust"));
 
    hello(&m);
    hello(&(*m)[..]);
}
 
fn hello(name: &str) {
    println!("Hello, {}!", name);
}
  • Implicit Deref Coercion: &MyBox<String> -> &String -> &str

  • &(*m) dereferences the MyBox<String>, then & creates a reference to the String, and [..] gets a slice of String.

⚠️

Rust cannot perform deref coercion:

From &T(immutable reference) to &mut U (mutable reference)

Drop Trait

  • drop trait allows you to customize the code that is run when an instance of the smart pointer goes out of scope.

  • It automatically runs when the value goes out of scope.

struct CustomSmartPointer {
    data: String,
}
 
impl Drop for CustomSmartPointer {
    fn drop(&mut self) {
        println!("Dropping CustomSmartPointer with data `{}`!", self.data);
    }
}
 
fn main() {
    let c = CustomSmartPointer { data: String::from("my stuff") };
    let d = CustomSmartPointer { data: String::from("other stuff") };
    println!("CustomSmartPointers created.");
    drop(d);
    println!("CustomSmartPointer dropped before the end of main.");
}
Execution Output
CustomSmartPointers created.
Dropping CustomSmartPointer with data `other stuff`!
CustomSmartPointer dropped before the end of main.
Dropping CustomSmartPointer with data `my stuff`!

© 2026 Driptanil Datta. All rights reserved.

Software Developer & Engineer

Disclaimer:The content provided on this blog is for educational and informational purposes only. While I strive for accuracy, all information is provided "as is" without any warranties of completeness, reliability, or accuracy. Any action you take upon the information found on this website is strictly at your own risk.

Copyright & IP:Certain technical content, interview questions, and datasets are curated from external educational sources to provide a centralized learning resource. Respect for original authorship is maintained; no copyright infringement is intended. All trademarks, logos, and brand names are the property of their respective owners.

System Operational

Built with Love ❤️ | Last updated: Mar 16 2026