Dynamically Sized Type

From Rust Community Wiki
Jump to navigation Jump to search

In Rust, most types have a size known at compile time - an i64 is 8 bytes, a char is 4 bytes, and so on. These types all implement the Sized trait. However, not all types do - some types have a size that is only known at runtime. These types are called unsized types, dynamically sized types, or DSTs.

There are four kinds of DSTs in Rust:

  • Trait objects (dyn Trait). As they can be any type, their size is dynamic.
  • Slices ([T]). They can contain any number of elements.
  • The string slice type (str). String slices can contain any number of bytes.
  • Structs and tuples that contain a DST.

As DSTs are unsized, they can't be held on the stack like regular variables – you must store a pointer to them; this can be a reference or a container like BoxThis links to official Rust documentation or ArcThis links to official Rust documentation.

Pointers and references to DSTs in Rust are "fat" – they are twice as wide as a regular pointer, because they contain more information than just their memory address. Trait objects also store a pointer to the virtual method table, other DSTs store their length in addition to their memory address.

At the moment, DSTs are very hard to work with mostly because they can't be stored on the stack, which prevents owning or moving a DST without allocating it on the heap. This will hopefully get better in the future – at the moment, try to avoid them.

Dynamically sized structs and tuples[edit | edit source]

Structs and Tuples are allowed to contain at most one DST. If they do, they are dynamically sized as well. The DST must be the very last field in the struct or tuple. For example:

// T might be unsized
struct Foo<'a, T: ?Sized> {
    a: &'a T,
    b: (usize, T),
}

In this example, the field a is sized, since T is behind a reference. However, b, and in effect Foo, can be unsized. For example, Foo<str> is unsized.

Examples[edit | edit source]

The standard library contains a few dynamically-sized structs, for example:

Sized bounds[edit | edit source]

Because DSTs are so difficult to work with, all generics implicitly require T: Sized. To either require unsized types or to support both you can write T: !Sized or T: ?Sized respectively; for example Box has ?Sized which allows it to store both sized and unsized values.