Smart Pointer
-
A pointer is a variable that stores the memory address, which points to any other data in the memory.
-
Most common pointer is the reference (only simply borrow the value, they point to, i.e. doesn't have ownership of the value).
-
References doesn't have any special capabilities, i.e. they doesn't have overhead (unlike pointers).
-
Smart Pointers are data structures that act like pointers, but also have additional metadata and capabilities.
-
In many cases, smart pointers have ownership of the data they point to (unlike references, which simply borrow values).
-
Smart pointers are implemented using structs (they implement the
deref&droptraits). -
dereftrait allows an instance of the smart pointer struct to behave like a reference, so that you can write code that works with either references or smart pointers. -
droptrait allows you to customize the code that is run when an instance of the smart pointer goes out of scope.
Box Smart Pointer
fn main() {
let b = Box::new(5);
println!("b = {}", b);
}-
Boxis a smart pointer, it stores address of the heap memory, where the value5is stored. -
Boxes doesn't have overhead, except storing data in the heap (but they have very limited capabilities)
Usage:
Boxes are useful when you have a type whose size can't be known at compile time and you want to use a value of that type in a context that requires an exact size.
Example:
enum List {
Cons(i32, List),
Nil,
}
use List::{ Cons, Nil };
fn main() {
let list = Cons(1, Cons(2, Cons(3, Nil)));
}recursive type List has infinite size
2 | Cons(i32, List),
| ---- recursive without indirectionCons()constructs memory objects which hold two values or pointers to two values.
Fix:
enum List {
Cons(i32, Box<List>),
Nil,
}
use List::{ Cons, Nil };
fn main() {
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}Boxinstead of calculating the size of the type at compile time, it stores the address of the heap memory, where the value is stored.
Deref Trait
dereftrait allows an instance of the smart pointer struct to behave like a reference, so that you can write code that works with either references or smart pointers.
fn main() {
let x = 5;
let y = &x;
assert_eq!(5, x);
assert_eq!(5, y);
}Error: no implementation for {integer} == &{integer}
fn main() {
let x = 5;
let y = &x;
assert_eq!(5, x);
assert_eq!(5, *y);
}*ydereferences the referenceyto get the value it points to.
Deref Trait with Smart Pointers:
fn main() {
let x = 5;
let y = Box::new(x);
assert_eq!(5, x);
assert_eq!(5, *y);
}-
Boxsmart pointer implements thedereftrait, which allows it to be dereferenced like a reference. -
Here
*ydereferences theBoxsmart pointer, to get the value from the address storing the duplicate value stored in heap.
Custom Smart Pointer
- Implementing the
Dereftrait on your own data structures allows you to use all the methods provided by the standard library.
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
fn main() {
let x = 5;
let y = MyBox::new(x);
assert_eq!(5, x);
assert_eq!(5, *y);
}Implicit Deref Coercions
-
Implicit Deref Coercion is a convenience that Rust performs on arguments to functions and methods which implement the
Dereftrait. -
It is used to convert a reference of one type into a reference of another type.
fn main() {
let m = MyBox::new(String::from("Rust"));
hello(&m);
hello(&(*m)[..]);
}
fn hello(name: &str) {
println!("Hello, {}!", name);
}-
Implicit Deref Coercion:
&MyBox<String>->&String->&str -
&(*m)dereferences theMyBox<String>, then & creates a reference to the String, and[..]gets a slice ofString.
Rust cannot perform deref coercion:
From &T(immutable reference) to &mut U (mutable reference)
Drop Trait
-
droptrait allows you to customize the code that is run when an instance of the smart pointer goes out of scope. -
It automatically runs when the value goes out of scope.
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
}
}
fn main() {
let c = CustomSmartPointer { data: String::from("my stuff") };
let d = CustomSmartPointer { data: String::from("other stuff") };
println!("CustomSmartPointers created.");
drop(d);
println!("CustomSmartPointer dropped before the end of main.");
}CustomSmartPointers created.
Dropping CustomSmartPointer with data `other stuff`!
CustomSmartPointer dropped before the end of main.
Dropping CustomSmartPointer with data `my stuff`!