Fundamentals 9 min read

Mastering Rust Smart Pointers: Box, RefCell, Rc/Arc, Mutex & RwLock Explained

This tutorial walks through Rust's core smart pointers—Box, RefCell, Rc/Arc, Mutex, and RwLock—showing how to set up the environment, use each pointer with concrete code examples, compare their concurrency behavior, and answer common questions about their proper usage.

Dunmao Tech Hub
Dunmao Tech Hub
Dunmao Tech Hub
Mastering Rust Smart Pointers: Box, RefCell, Rc/Arc, Mutex & RwLock Explained

Introduction

This article is part of a series aimed at consolidating Rust knowledge by documenting the language's extensive smart‑pointer ecosystem.

Environment Setup

Beyond a basic Cargo installation, no extra dependencies are required because the smart pointers used are all from the standard library. A minimal Cargo.toml example is shown.

[package]
name = "ref_test"
version = "0.1.0"
edition = "2024"

[dependencies]

Code Walkthrough

Box

Box allocates a fixed‑size value on the heap and stores a pointer to it. The example creates a Box containing the integer 5 and prints both the stack address of the Box variable and the heap address of the contained value.

let box1 = Box::new(5);
println!("stack pointer:{:p}", &box1);
println!("heap pointer:{:p}", &*box1);
stack pointer:0x7ffc262e85a8 heap pointer:0x55e588148d00

Key point: Box provides a stable heap allocation for a value.

RefCell

RefCell enables interior mutability: it allows mutable borrowing of data that is otherwise immutable at compile time. The example creates a RefCell holding 5, mutably borrows it, modifies the value, and then extracts the final value.

let rfc = RefCell::new(5);
{
    let mut p = rfc.borrow_mut();
    *p += 5;
}
println!("final value:{:?}", rfc.into_inner());
final value:10

Key point: RefCell guarantees at most one mutable borrow at a time, ensuring thread‑safety for single‑threaded code.

Rc / Arc

Rc provides reference‑counted shared ownership for single‑threaded scenarios, while Arc does the same across threads. The examples demonstrate cloning an Rc, printing pointer addresses, and using Arc with multiple threads to show reference counts.

let c = Rc::new(5);
let p = Rc::clone(&c);
println!("{}", p);
println!("c address:{:p}", &c);
println!("c data address:{:p}", &*c);
println!("p address:{:p}", &p);
println!("p data address:{:p}", &*p);
c address:0x7ffc21c5f780 c data address:0x5566c676cd10 p address:0x7ffc21c5f788 p data address:0x5566c676cd10
let arc_data = Arc::new(5);
let mut handles = vec![];
for _i in 0..3 {
    let c = Arc::clone(&arc_data);
    let d = thread::spawn(move || {
        println!("c value:{}", c);
        println!("ref count:{}", Arc::strong_count(&c));
    });
    handles.push(d);
}
for handle in handles {
    handle.join().unwrap();
}
println!("final ref count:{}", Arc::strong_count(&arc_data));
c value:5 ref count:4 final ref count:1

Key point: Rc/Arc allow multiple owners without changing the underlying data address, using reference counting.

Mutex

Mutex provides exclusive access to data across threads. Combined with Arc, it enables safe mutable updates from several threads. The example spawns three threads, each adding its index to a shared integer, resulting in a final value of 8.

let p = Arc::new(Mutex::new(5));
let mut handles = vec![];
for i in 0..3 {
    let updater = Arc::clone(&p);
    let c = thread::spawn(move || {
        let mut v = updater.lock().unwrap();
        *v += i;
    });
    handles.push(c);
}
for handle in handles {
    handle.join().unwrap();
}
println!("final result:{}", p.lock().unwrap());
final result:8

Key point: Mutex ensures only one thread can modify the protected data at a time.

RwLock

RwLock is similar to Mutex but distinguishes read‑many/write‑few scenarios: multiple readers may hold the lock concurrently, while writers obtain exclusive access. The example shows threads acquiring a write lock, updating a value, releasing it, then reading the final result.

use std::{sync::{Arc, RwLock}, thread};

let p = Arc::new(RwLock::new(5));
let mut handles = vec![];
for i in 0..3 {
    let updater = Arc::clone(&p);
    let c = thread::spawn(move || {
        let mut w = updater.write().unwrap();
        *w += i;
        drop(w); // release write lock
        println!("{}", updater.read().unwrap());
    });
    handles.push(c);
}
for i in handles {
    i.join().unwrap();
}
println!("final value:{}", p.read().unwrap());

FAQ

Why use RwLock when Mutex already provides mutual exclusion? Mutex enforces a “total lock” – only one thread may access the data, regardless of read or write. RwLock separates read and write paths, allowing many concurrent readers while still guaranteeing exclusive writes.

Conclusion

The article records Rust’s smart pointers, explaining how each type addresses different sharing and mutability requirements. By combining these pointers appropriately, developers can handle both single‑threaded and multi‑threaded data‑sharing scenarios safely and efficiently.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Rustmutexsmart pointersARCBoxRCrwlockrefcell
Dunmao Tech Hub
Written by

Dunmao Tech Hub

Sharing selected technical articles synced from CSDN. Follow us on CSDN: Dunmao.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.