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.