DEV Community

Cover image for Fat Pointers & Smart Pointers in Rust
Lozi
Lozi

Posted on

Fat Pointers & Smart Pointers in Rust

When transitioning from C or C++ to Rust, the hardest habit to break is how you think about memory. In C/C++, pointers are mostly just memory addresses. Rust adds ownership and richer pointer types to make memory access safe without sacrificing performance.
In order to understand Rust, you have to know 2 key concepts: Smart Pointers and Fat Pointers. The entire decision of which to use depends on one rule: Ownership and Borrowing.

Smart Pointer: Definition

A data structure that acts like a normal pointer, but it comes with a "Property Manager" (Ownership Manager), let me explain:

  • It owns some resource (commonly heap memory) and knows exactly when that resource is no longer needed.

In Rust, common Smart Pointers have Vec<T>, Box<T>, or String.

Why is it useful?

  • The CPU Stack is really fast but tiny. So if you try to create a 3000000 byte array on the stack, your program will crash instantly raising a Stack Overflow. Smart pointers allocate their data on the heap.
  • Some smart pointers, like Vec<T> and String, can dynamically grow or shrink.
  • No memory leaks: Smart pointers automatically clean up the resources they own when they go out of scope, so you don't need to call free() manually!!!.

The Fat Pointer (Borrower & Viewer)

A Fat Pointer is a "borrowed" reference to a contiguos sequence of memory. Conceptually, a fat pointer is like a small struct containing:

  • The Address: Where the data starts.
  • The length: How many elements exist in the data.

In Rust, Fat Pointers are represented as Slices (e.d., &[u8] or &str).

Why is this useful?

  • Zero-Cost Windows: If you have a 500MB file in RAM, you don't want to duplicate data just to look at the header. A Fat Pointer create a "window" into that existing file. It requieres zero memory allocation or zero copying. Which is very useful.
  • Bounds Safety: Because the Fat Pointer has it's own length, the Because the fat pointer carries its length, Rust performs a bounds check before doing the indexing. If the index is out of range, the program safely panics instead of reading invalid memory.

Rust vs C/C++

The reason why these concepts feel weird at first is because C & C++ rely almost entirely on Naked Pointers (const u8*)
A naked Pointer is just a note with an address (0x7FFF0042), so it has no conext, length and no context.

First difference: Bounds checking

  • In C/C++ when you write ptr[500], the CPU does the math (ptr+500) and walks blindly 500 steps past the address. So if your array was only 15 bytes long, you may read unrelated memory from your own process or trigger a segmentation fault.
  • Rust: When you write slice[500], Rust automatically checks whether 500 is within the slice's length. If it's out of bounds, the program safely stops itself.

Second Difference: Memory Leaks

C/C++: Heap allocations are not automatically cleaned up. Even if a pointer variable goes out of scope, the allocated memory stays alive until you explicitly call free() or delete. If you forget, the program leaks memory.
Rust: By using a Smart Pointer (Vec), Rust automatically inserts calls to drop when ownership ends. As a result, you can't accidentally forget to free memory.

Third Difference: Creating vs. Passing

  • C/C++: You pass naked pointers everywhere. You pass them in order to create data, and also to read it.
  • In Rust: You use Smart Pointers (Vec) only when you need to create and own data. So immediately after, you slice them into Fat Pointers in order to pass to your functions so they can efficiently borrow and read the data.

Top comments (0)