Most types have a particular size, in bytes, that is knowable at compile time.
For example, an i32
`i32is thirty-two bits big, or four bytes. However, there are some types which are useful to express, but do not have a defined size. These are called ‘unsized’ or ‘dynamically sized’ types. One example is
[T]. This type represents a certain number of
T` in sequence. But we don’t know how many
there are, so the size is not known.
Rust understands a few of these types, but they have some restrictions. There are three:
&[T]
`&[T]works just fine, but a
[T]` does not.struct
`struct` may have a dynamically sized type; the
other fields must not. Enum variants must not have dynamically sized types as
data.So why bother? Well, because [T]
`[T]` can only be used behind a pointer, if we
didn’t have language support for unsized types, it would be impossible to write
this:
impl Foo for str {
or
fn main() { impl<T> Foo for [T] { }impl<T> Foo for [T] {
Instead, you would have to write:
fn main() { impl Foo for &str { }impl Foo for &str {
Meaning, this implementation would only work for references, and not
other types of pointers. With the impl for str
, all pointers, including (at
some point, there are some bugs to fix first) user-defined custom smart
pointers, can use this impl
`impl`.
If you want to write a function that accepts a dynamically sized type, you
can use the special bound, ?Sized
`?Sized`:
struct Foo<T: ?Sized> { f: T, }
This ?
`?, read as “T may be
Sized”, means that this bound is special: it lets us match more kinds, not less. It’s almost like every
Timplicitly has
T: Sized, and the
?` undoes this default.