In this lesson, you will learn why certain types in Rust don't have a known size at compile time and how pointers allow us to work with them safely.
What is a Dynamically Sized Type (DST)?
Most types in Rust—like i32, bool, or structs—have a size known at compile time. This is "Sized." However, some types, like slices ([T]) or trait objects (dyn Trait), do not. These are Dynamically Sized Types (DSTs).
Because the compiler doesn't know how much memory these types occupy, you cannot store them directly on the stack or pass them by value. You cannot write let x: [i32] = ...; because the compiler needs to know exactly how many bytes x takes up to allocate stack space.
The Role of Pointers
To use DSTs, we must use a pointer. In Rust, when you create a reference to a DST, you aren't just getting a simple memory address (the raw pointer). You get a wide pointer (or fat pointer).
A wide pointer consists of two parts:
- The memory address of the data.
- The "metadata" required to understand the data.
For a slice [T], the metadata is the length of the slice. For a trait object dyn Trait, the metadata is a pointer to a vtable (a table of function pointers) that tells Rust which specific implementations of the trait methods to call.
fn main() {
let array = [1, 2, 3];
// This is a slice (DST). We access it through a reference (&[i32]).
// The reference is a "wide pointer" containing the address + length (3).
let slice: &[i32] = &array;
println!("Length: {}", slice.len());
}
Why This Matters
When you use a type like Box<dyn Trait> or &[T], you are leveraging the fact that the pointer itself has a known, fixed size (the address plus the metadata), even if the data it points to is unsized. This allows you to write flexible, polymorphic code without sacrificing memory safety or performance.
Key Takeaway: Because DSTs have an unknown size, they must always be accessed behind a pointer, which stores the data's address along with the necessary metadata to interpret it.