Unlock Your Python Backend Career: Build 30 Projects in 30 Days. Join now for just $54

Rust Advanced

Advanced Rust

Rust Advanced

Smart pointers in Rust

Smart pointers in Rust

In static languages, a pointer is a variable that stores an address in memory. We have seen and used the most common type of pointers in Rust: the reference. Smart pointers are another kind of pointers in Rust. The difference between references and smart pointers is that they own the data they point to, while references only borrow the data. The String and Vec<T> types are smart pointers.

Smart pointers are usually implemented using structs, automatically implementing the Deref and Drop traits. We will see the following smart pointers: Box, Rc, Ref, and Arc. Let’s get started.

Box smart pointer

The Box smart pointer stores data on the heap instead of the stack and then leaves a pointer to the heap on the stack. Box smart pointers are used in the following scenarios:

  • when the size of a variable type is unknown at compile time, but the variable is used for other operations

  • when you want to prevent copying for large data whose ownership is being transferred, etc.

An example of creating a Box smart pointer is given below:

fn main() {
    let x = Box::new(5); // Allocate an integer on the heap and store a Box pointer on the stack
    println!("Value: {}", x);
}

Box automatically deallocates the memory when the pointer goes out of scope. It also implements the Deref trait, allowing you to use it similarly to a regular reference.

Rc<T> and Arc<T> Smart Pointers

Rc<T> stands for "Reference Counted" and Arc<T> stands for "Atomic Reference Counted." These smart pointers allow multiple parts of your code to share data ownership. They keep track of the number of references to the data, and the data is only deallocated when the last reference is dropped.

Rc<T> is not thread-safe, so it's suitable for use in a single-threaded context. Arc<T>, on the other hand, adds atomic operations to ensure thread safety, making it suitable for multi-threaded environments.

Example:

use std::rc::Rc;
use std::sync::Arc;

fn main() {
    let shared_data = Rc::new(42); // Create a reference-counted pointer
    let thread_safe_data = Arc::new(42); // Create an atomic reference-counted pointer
}

RefCell<T> Smart Pointer

RefCell<T> is a smart pointer allowing dynamic data borrowing. It enforces Rust's borrowing rules at runtime rather than compile time. This enables you to have multiple mutable references to the same data as long as you follow the rules. It's commonly used when you need interior mutability, meaning you want to mutate data even if you have an immutable reference.

Example:

use std::cell::RefCell;

fn main() {
    let data = RefCell::new(5);
    let mut mut_ref = data.borrow_mut();
    *mut_ref = 10;
}

Remember that RefCell enforces borrowing rules at runtime, so if you violate them, your program will panic.

Whenever you're ready

There are 4 ways we can help you become a great backend engineer:

The MB Platform

Join 1000+ backend engineers learning backend engineering. Build real-world backend projects, learn from expert-vetted courses and roadmaps, track your learnings and set schedules, and solve backend engineering tasks, exercises, and challenges.

The MB Academy

The “MB Academy” is a 6-month intensive Advanced Backend Engineering BootCamp to produce great backend engineers.

Join Backend Weekly

If you like post like this, you will absolutely enjoy our exclusive weekly newsletter, Sharing exclusive backend engineering resources to help you become a great Backend Engineer.

Get Backend Jobs

Find over 2,000+ Tailored International Remote Backend Jobs or Reach 50,000+ backend engineers on the #1 Backend Engineering Job Board