mirror of https://github.com/rust-lang/nomicon
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
70 lines
2.4 KiB
70 lines
2.4 KiB
10 years ago
|
% 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.
|
||
|
|
||
|
# Dynamically Sized Types (DSTs)
|
||
|
|
||
|
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.
|
||
|
|
||
|
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.**
|
||
|
|
||
|
|
||
|
|
||
|
# Zero Sized Types (ZSTs)
|
||
|
|
||
|
Rust actually allows types to be specified that occupy *no* space:
|
||
|
|
||
|
```rust
|
||
|
struct Foo; // No fields = no size
|
||
|
enum Bar; // No variants = no size
|
||
|
|
||
|
// All fields have no size = no size
|
||
|
struct Baz {
|
||
|
foo: Foo,
|
||
|
bar: Bar,
|
||
|
qux: (), // empty tuple has no size
|
||
|
}
|
||
|
```
|
||
|
|
||
|
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
|
||
|
context.
|
||
|
|
||
|
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.
|
||
|
|
||
|
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
|
||
|
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.
|
||
|
|
||
|
|