Smart pointers are pointers with extended capabilities, such as bound checks and automatic memory management. Smart pointers also include additional metadata. Now, the concept of smart pointers is not new, especially to C++ programmers.
For this article, we will explore various smart pointers as defined in the Rust standard library.
Key Points
In Rust, smart pointers are data structures however, unlike normal pointers, smart pointers can actually own data. They are commonly implemented using structs but differ from normal structs in the sense that they implement Deref and Drop traits.
There are 5 smart pointers to know in Rust:
- Box<T>
- Deref <T>
- Drop <T>
- Rc<T>
- RefCell<T>
NOTE: In Rust <T> refers to the data type.
Keep in mind that will not cover all smart pointers in this article.
Box <T>
The Box<T> smart pointer provides a way to reference data stored in the heap memory. By default, Rust allocates everything on the stack memory. Using the box smart pointer, we can access data on the heap, incurring no performance overhead.
The box smart pointer also obeys the rules of ownership and borrowing. Hence, it has a single owner of data and can have only one mutable reference and any number of immutable references.
// Box<T>
let var = Box::new(100);
println!("Value: {}", var)
}
In the example above, we have a box smart pointer that stores the value 5 in the heap. Keep in mind that the box itself is stored in the stack.
Once the program is terminated, the box is deallocated from the memory.
Deref <T>
As the name suggests, the Deref <T> smart pointer is used to allow manipulation of the dereferencing operation. Using the deref smart pointer, you can create a struct that can work with both smart pointer and references.
An example code is as shown below:
struct CustBox<T> {
data: T,
}
impl<T> Deref for CustBox<T> {
// generic type param
type Target = T;
fn deref(&self) ->&T{
&self.data
}
}
fn main() {
let x = CustBox{data: 100};
println!("Value: {}",*(x.deref()))
}
Drop <T>
The Drop smart pointer is used to free memory that is allocated in the heap. The Drop trait in Rust handles free the memory when a variable goes out of scope. We do not need to call this trait manually, as Rust will do this for us. However, we can implement it for custom types as shown.
x: i32,
}
impl Drop for MyStruct {
fn drop(&mut self) {
println!("Dropping: {}", self.x)
}
}
fn main() {
let _x = MyStruct{x: 100};
let _y = MyStruct{x: 200};
}
The above implements the Drop trait for custom trait. The output is as shown:
Dropping: 200
Dropping: 100
Conclusion
In this article, we cover how to create and work with common smart pointers in the Rust programming language. You can learn more about Rust smart pointers in the resource below: