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.
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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Dunmao Tech Hub
Sharing selected technical articles synced from CSDN. Follow us on CSDN: Dunmao.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
