We've talked about the stack, and how it is the place where Rust stores data and keeps track of what needs to be kept around and what needs to be cleaned up. It's a powerful, useful mechanism, but it's not right for everything.
Imagine we have a variable that contains an image. It takes up several megabytes of memory, and we need to transfer ownership of it between various parts of our program at different times. If we just put it on the stack, and allow Rust to move it into new scopes as needed, everything will work, but it will be slowed down by the need to copy those megabytes of data every time it moves the value to a new owner.
That's not the only scenario where storing information on the stack isn't ideal, but it's a good illustration.
On the other hand, the last thing we want to do is to break the stack and scope-based ownership model, which gives Rust so much of its power.
Fortunately, there's a way to store data outside of the stack, and still have it act as though it were part of a scope: smart pointers.
The Rust standard library includes several different kinds of smart pointer, meant to address different needs. Smart pointer values themselves are stored in the stack, just like other data values are, but they include the necessary instrumentation to allocate a chunk of heap memory when they are created, and release it back to the system when their lifetimes end. A data value stored in that heap memory can be accessed through the smart pointer, as if it was stored inside the smart pointer.
Thanks to smart pointers, the lifetime of values stored in heap memory mirror the lifetimes of values that follow Rust's normal rules, but with the big advantage that the section of heap memory does not have to be copied when the smart pointer is moved to a new scope. Our multi-megabyte image can be moved around between scopes for the cost of moving a few bytes, because the image itself does not have to move, just the smart pointer that controls it.