Smart pointers are data structures that act like pointers but also have additional metadata and capabilities
Simple zero-overhead pointers are also present in Rust: references
To understand why smart-pointers are used we need to go back to some Rust fundamentals
We already met ownership, but here is a quick recap:
A snippet present in 99% of C programs is the following:
int *arr = malloc(sizeof(int) * arr_len);
Promptly ignoring to check for NULL
The above code allocates on the heap an array
Simple enough... no?
Rust forces us to think more about what allocating on the heap implies:
We can see that references can answer two out of three questions:
However references borrow data, they do not own it
Smart pointers, on the other hand, can answer all questions:
For friends and family, RAII, is a pattern that ties allocation/deallocation (resources in general) to an object's lifetime
When we create an object we allocate our resources
When our object goes out of scope, we deallocate (drop) them
This is exactly what ownership enforces
RAII is essential to avoid memory leaks:
int *arr = malloc(sizeof(int) * arr_len);
arr = NULL; // Now the memory is lost but allocated
The above isn't possible in Rust since ownership can only be transferred
Types like String
and Vec<T>
are smart pointers
We haven't noticed since derefencing is coerced when possible
Box
The most straightforward type of smart pointer:
Box<T>
They allow us to store data one the heap, without additional
performace loss or features
Boxes are often used:
We have avoided talking about them two times
Simply put, they are
objects of dynamic type that implement a specific trait and do
dynamic dispatch
// Dynamic dispatch
// vvvvvvvvvvv
Box<dyn MyTrait>
// ^^^
// Dynamic size
Rc<T>
I swear it is not a typo... It stands for Reference Counted
This smart pointer is used when we want to enable multiple ownership
The Rc<T>
type
keeps track of the number of references to a value to determine
whether or not the value is still in use.
If there are zero references to a value, the value can be cleaned
up without any references becoming invalid.
Arc<T>
Arc
is Rc
's cousin
They both do reference counting, however...
Rc
does the countingArc
does the counting in a concurrent-safe way
This is not all that smart pointers have to offer, but just an appetizer
If you want to delve more into the topic, a very good starting point is reading the Rust's book chapter on the topic