rust/src/doc/tarpl/exotic-sizes.md

90 lines
2.7 KiB
Markdown
Raw Normal View History

2015-07-06 18:36:16 -07:00
% Exotically Sized Types
Most of the time, we think in terms of types with a fixed, positive size. This
is not always the case, however.
2015-07-07 09:39:21 -07:00
2015-07-06 18:36:16 -07:00
# Dynamically Sized Types (DSTs)
2015-07-14 09:56:10 -07:00
Rust also supports types without a statically known size. On the surface, this
is a bit nonsensical: Rust *must* know the size of something in order to work
with it! DSTs are generally produced as views, or through type-erasure of types
that *do* have a known size. Due to their lack of a statically known size, these
types can only exist *behind* some kind of pointer. They consequently produce a
*fat* pointer consisting of the pointer and the information that *completes*
them.
For instance, the slice type, `[T]`, is some statically unknown number of
elements stored contiguously. `&[T]` consequently consists of a `(&T, usize)`
pair that specifies where the slice starts, and how many elements it contains.
Similarly, Trait Objects support interface-oriented type erasure through a
`(data_ptr, vtable_ptr)` pair.
2015-07-06 18:36:16 -07:00
Structs can actually store a single DST directly as their last field, but this
makes them a DST as well:
```rust
// Can't be stored on the stack directly
struct Foo {
info: u32,
data: [u8],
}
```
**NOTE: As of Rust 1.0 struct DSTs are broken if the last field has
a variable position based on its alignment.**
2015-07-07 09:39:21 -07:00
2015-07-06 18:36:16 -07:00
# Zero Sized Types (ZSTs)
Rust actually allows types to be specified that occupy *no* space:
```rust
struct Foo; // No fields = no size
// All fields have no size = no size
struct Baz {
foo: Foo,
2015-07-14 09:56:10 -07:00
qux: (), // empty tuple has no size
2015-07-07 09:39:21 -07:00
baz: [u8; 0], // empty array has no size
2015-07-06 18:36:16 -07:00
}
```
2015-07-14 09:56:10 -07:00
On their own, ZSTs are, for obvious reasons, pretty useless. However as with
many curious layout choices in Rust, their potential is realized in a generic
2015-07-06 18:36:16 -07:00
context.
2015-07-14 09:56:10 -07:00
Rust largely understands that any operation that produces or stores a ZST can be
reduced to a no-op. For instance, a `HashSet<T>` can be effeciently implemented
as a thin wrapper around `HashMap<T, ()>` because all the operations `HashMap`
normally does to store and retrieve keys will be completely stripped in
monomorphization.
2015-07-06 18:36:16 -07:00
Similarly `Result<(), ()>` and `Option<()>` are effectively just fancy `bool`s.
Safe code need not worry about ZSTs, but *unsafe* code must be careful about the
2015-07-14 09:56:10 -07:00
consequence of types with no size. In particular, pointer offsets are no-ops,
and standard allocators (including jemalloc, the one used by Rust) generally
consider passing in `0` as Undefined Behaviour.
2015-07-06 18:36:16 -07:00
2015-07-07 09:39:21 -07:00
2015-07-14 09:56:10 -07:00
# Empty Types
2015-07-07 09:39:21 -07:00
Rust also enables types to be declared that *cannot even be instantiated*. These
types can only be talked about at the type level, and never at the value level.
```rust
2015-07-14 09:56:10 -07:00
enum Foo { } // No variants = EMPTY
2015-07-07 09:39:21 -07:00
```
2015-07-14 09:56:10 -07:00
TODO: WHY?!