kota's memex

A pointer is a concept for a variable that contains an address in memory. This address refers to, or points at, some other data. The most common kind of pointer is rust is a reference, which are indicated with the & symbol. They borrow the value they point to and have no overhead.

See rust ownership for details on references.

Smart pointers on the other hand, are data structures that act like a pointer, but also have additional metadata and capabilities. Rust has a variety of smart pointers defined in the standard library that provide functionality beyond that provided by references.

uses

In rust, at compile time, you must be able to evaluate the stack size of every data structure. This makes it quite tricky to represent a recursive data structure...

In rust we can works around this with a bit of indirection using the Box type which allows us to allocate the value on the heap and store a pointer to its location, of a known size, on the stack.

ownership

References only ever borrow data while smart pointers often own the data they point to. String and Vec<T> are both super common smart pointers. They own some memory, but allow you to manipulate it. They also have extra metadata and capabilities. String stores its capacity as metadata and has the extra ability to ensure its data will always be valid UTF-8.

implementation and traits

Smart pointers are usually implemented as structs with the Deref and Drop traits. The Deref trait allows an instance of the smart pointer struct to behave like a reference so you can write your code to work with either references or smart pointers. The Drop trait allows you to customize the code that's run when an instance of the smart pointer goes out of scope.

Box<T>

A very simple smart pointer that allows storing data on the head rather than the stack. Boxes don't have performance overhead, other than storing the data on the heap, but they also don't have any additional capabilities either.

let b = Box::new(5);
println!("b = {b}");

Deref

Here's a quick example of our own Box implementation that supports Deref and can thus be used like a pointer. Notably, it does not actually store its data on the heap!

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) -> &Self::Target {
        &self.0
    }
}

fn main() {
    let x = 5;
    let y = MyBox::new(x);

    assert_eq!(5, x);
    assert_eq!(5, *y);
}