Rust Lesson 22 – TITLE HERE | Dataplexa

Smart Pointers in Rust

In this lesson, you will learn about smart pointers in Rust. Smart pointers are data structures that act like pointers but also provide extra features such as ownership rules, automatic cleanup, and shared access.

Rust’s smart pointers help you write safe and efficient programs, especially when working with heap memory, sharing data, or building complex data structures.


What Is a Smart Pointer?

A smart pointer is a type that stores a value in memory (often on the heap) and also includes metadata and behavior about how that value is used.

Many smart pointers implement two important traits:

  • Deref — lets you treat the smart pointer like a reference
  • Drop — runs cleanup code automatically when the value goes out of scope

The Most Common Smart Pointers

Rust provides multiple smart pointers, each for different needs. The most common ones are:

  • Box<T> — heap allocation with single ownership
  • Rc<T> — reference counting (shared ownership in single-threaded code)
  • Arc<T> — atomic reference counting (shared ownership across threads)

Box<T>: Heap Allocation

Box<T> stores a value on the heap instead of the stack. This is useful when:

  • The value is large and you want to avoid copying it on the stack
  • The size is unknown at compile time (e.g., recursive types)
  • You want a stable memory location
let num = Box::new(100);

println!("{}", num);

Even though the data is on the heap, Rust still manages ownership normally. When the box goes out of scope, the heap memory is automatically freed.


Box<T> Example with a Large Value

Sometimes large values are placed on the heap to keep the stack small and fast.

let big_array = Box::new([0; 1000]);

println!("{}", big_array[0]);

Using Deref (Access Like a Normal Reference)

Smart pointers like Box<T> implement Deref, so you can use them like normal references.

let x = Box::new(String::from("Rust"));

println!("{}", x.len());

Drop Trait: Automatic Cleanup

Rust automatically calls cleanup logic when a value goes out of scope. This is done through the Drop trait.

struct Resource;

impl Drop for Resource {
    fn drop(&mut self) {
        println!("Resource cleaned up!");
    }
}

fn main() {
    let r = Resource;
    println!("Using resource...");
} // drop runs here

Why Not Use Regular References Always?

References (&T) borrow data temporarily. Smart pointers often own data, and that ownership is what enables:

  • Heap allocation
  • Shared ownership (Rc/Arc)
  • Automatic cleanup through Drop

Rc<T> and Arc<T> Preview

Rust also provides smart pointers for sharing ownership:

  • Rc<T> — allows multiple owners in single-threaded programs
  • Arc<T> — allows multiple owners across threads safely

We will explore these deeply in the next lesson.


📝 Practice Exercises


Exercise 1

Create a Box<i32> and print the value.

Exercise 2

Create a box storing a string and print its length.

Exercise 3

Create a struct that implements Drop and prints a message on cleanup.

Exercise 4

Explain when you would choose a smart pointer instead of a reference.


✅ Practice Answers


Answer 1

let x = Box::new(42);
println!("{}", x);

Answer 2

let name = Box::new(String::from("Dataplexa"));
println!("{}", name.len());

Answer 3

struct Temp;

impl Drop for Temp {
    fn drop(&mut self) {
        println!("Temp dropped!");
    }
}

fn main() {
    let _t = Temp;
    println!("Inside main");
}

Answer 4

Use smart pointers when you need ownership + extra behavior (heap allocation, shared ownership, or automatic cleanup). Use references when you only need temporary borrowing.


What’s Next?

In the next lesson, you will learn about Box, Rc, and Arc in detail and how Rust supports shared ownership safely.