]> git.proxmox.com Git - rustc.git/blame - src/doc/nomicon/src/exotic-sizes.md
New upstream version 1.55.0+dfsg1
[rustc.git] / src / doc / nomicon / src / exotic-sizes.md
CommitLineData
8bb4bdeb 1# Exotically Sized Types
c1a9b12d 2
450edc1f
XL
3Most of the time, we expect types to have a statically known and positive size.
4This isn't always the case in Rust.
c1a9b12d 5
136023e0 6## Dynamically Sized Types (DSTs)
c1a9b12d 7
450edc1f 8Rust supports Dynamically Sized Types (DSTs): types without a statically
c1a9b12d
SL
9known size or alignment. On the surface, this is a bit nonsensical: Rust *must*
10know the size and alignment of something in order to correctly work with it! In
450edc1f
XL
11this regard, DSTs are not normal types. Because they lack a statically known
12size, these types can only exist behind a pointer. Any pointer to a
13DST consequently becomes a *wide* pointer consisting of the pointer and the
c1a9b12d
SL
14information that "completes" them (more on this below).
15
450edc1f
XL
16There are two major DSTs exposed by the language:
17
18* trait objects: `dyn MyTrait`
29967ef6 19* slices: [`[T]`][slice], [`str`], and others
c1a9b12d
SL
20
21A trait object represents some type that implements the traits it specifies.
b039eaaf 22The exact original type is *erased* in favor of runtime reflection
c1a9b12d 23with a vtable containing all the information necessary to use the type.
450edc1f
XL
24The information that completes a trait object pointer is the vtable pointer.
25The runtime size of the pointee can be dynamically requested from the vtable.
c1a9b12d
SL
26
27A slice is simply a view into some contiguous storage -- typically an array or
450edc1f
XL
28`Vec`. The information that completes a slice pointer is just the number of elements
29it points to. The runtime size of the pointee is just the statically known size
30of an element multiplied by the number of elements.
c1a9b12d
SL
31
32Structs can actually store a single DST directly as their last field, but this
33makes them a DST as well:
34
35```rust
36// Can't be stored on the stack directly
450edc1f 37struct MySuperSlice {
c1a9b12d
SL
38 info: u32,
39 data: [u8],
40}
41```
42
450edc1f
XL
43Although such a type is largely useless without a way to construct it. Currently the
44only properly supported way to create a custom DST is by making your type generic
45and performing an *unsizing coercion*:
46
47```rust
48struct MySuperSliceable<T: ?Sized> {
49 info: u32,
e1599b0c 50 data: T,
450edc1f
XL
51}
52
53fn main() {
54 let sized: MySuperSliceable<[u8; 8]> = MySuperSliceable {
55 info: 17,
56 data: [0; 8],
57 };
58
59 let dynamic: &MySuperSliceable<[u8]> = &sized;
60
61 // prints: "17 [0, 0, 0, 0, 0, 0, 0, 0]"
62 println!("{} {:?}", dynamic.info, &dynamic.data);
63}
64```
65
66(Yes, custom DSTs are a largely half-baked feature for now.)
67
136023e0 68## Zero Sized Types (ZSTs)
c1a9b12d 69
450edc1f 70Rust also allows types to be specified that occupy no space:
c1a9b12d
SL
71
72```rust
450edc1f 73struct Nothing; // No fields = no size
c1a9b12d
SL
74
75// All fields have no size = no size
450edc1f
XL
76struct LotsOfNothing {
77 foo: Nothing,
c1a9b12d
SL
78 qux: (), // empty tuple has no size
79 baz: [u8; 0], // empty array has no size
80}
81```
82
83On their own, Zero Sized Types (ZSTs) are, for obvious reasons, pretty useless.
84However as with many curious layout choices in Rust, their potential is realized
450edc1f
XL
85in a generic context: Rust largely understands that any operation that produces
86or stores a ZST can be reduced to a no-op. First off, storing it doesn't even
87make sense -- it doesn't occupy any space. Also there's only one value of that
88type, so anything that loads it can just produce it from the aether -- which is
c1a9b12d
SL
89also a no-op since it doesn't occupy any space.
90
450edc1f 91One of the most extreme examples of this is Sets and Maps. Given a
c1a9b12d
SL
92`Map<Key, Value>`, it is common to implement a `Set<Key>` as just a thin wrapper
93around `Map<Key, UselessJunk>`. In many languages, this would necessitate
94allocating space for UselessJunk and doing work to store and load UselessJunk
95only to discard it. Proving this unnecessary would be a difficult analysis for
96the compiler.
97
98However in Rust, we can just say that `Set<Key> = Map<Key, ()>`. Now Rust
99statically knows that every load and store is useless, and no allocation has any
100size. The result is that the monomorphized code is basically a custom
101implementation of a HashSet with none of the overhead that HashMap would have to
102support values.
103
104Safe code need not worry about ZSTs, but *unsafe* code must be careful about the
105consequence of types with no size. In particular, pointer offsets are no-ops,
e1599b0c 106and allocators typically [require a non-zero size][alloc].
c1a9b12d 107
e1599b0c
XL
108Note that references to ZSTs (including empty slices), just like all other
109references, must be non-null and suitably aligned. Dereferencing a null or
110unaligned pointer to a ZST is [undefined behavior][ub], just like for any other
111type.
c1a9b12d 112
5869c6ff 113[alloc]: ../std/alloc/trait.GlobalAlloc.html#tymethod.alloc
e1599b0c 114[ub]: what-unsafe-does.html
c1a9b12d 115
136023e0 116## Empty Types
c1a9b12d
SL
117
118Rust also enables types to be declared that *cannot even be instantiated*. These
119types can only be talked about at the type level, and never at the value level.
120Empty types can be declared by specifying an enum with no variants:
121
122```rust
123enum Void {} // No variants = EMPTY
124```
125
126Empty types are even more marginal than ZSTs. The primary motivating example for
450edc1f 127an empty type is type-level unreachability. For instance, suppose an API needs to
c1a9b12d
SL
128return a Result in general, but a specific case actually is infallible. It's
129actually possible to communicate this at the type level by returning a
130`Result<T, Void>`. Consumers of the API can confidently unwrap such a Result
131knowing that it's *statically impossible* for this value to be an `Err`, as
132this would require providing a value of type `Void`.
133
134In principle, Rust can do some interesting analyses and optimizations based
13cf67c4
XL
135on this fact. For instance, `Result<T, Void>` is represented as just `T`,
136because the `Err` case doesn't actually exist (strictly speaking, this is only
137an optimization that is not guaranteed, so for example transmuting one into the
138other is still UB).
139
140The following *could* also compile:
c1a9b12d 141
136023e0 142```rust,compile_fail
c1a9b12d
SL
143enum Void {}
144
145let res: Result<u32, Void> = Ok(0);
146
147// Err doesn't exist anymore, so Ok is actually irrefutable.
148let Ok(num) = res;
149```
150
13cf67c4 151But this trick doesn't work yet.
c1a9b12d
SL
152
153One final subtle detail about empty types is that raw pointers to them are
b039eaaf 154actually valid to construct, but dereferencing them is Undefined Behavior
450edc1f
XL
155because that wouldn't make sense.
156
157We recommend against modelling C's `void*` type with `*const Void`.
158A lot of people started doing that but quickly ran into trouble because
159Rust doesn't really have any safety guards against trying to instantiate
160empty types with unsafe code, and if you do it, it's Undefined Behaviour.
161This was especially problematic because developers had a habit of converting
162raw pointers to references and `&Void` is *also* Undefined Behaviour to
163construct.
164
165`*const ()` (or equivalent) works reasonably well for `void*`, and can be made
166into a reference without any safety problems. It still doesn't prevent you from
167trying to read or write values, but at least it compiles to a no-op instead
168of UB.
169
136023e0 170## Extern Types
450edc1f
XL
171
172There is [an accepted RFC][extern-types] to add proper types with an unknown size,
173called *extern types*, which would let Rust developers model things like C's `void*`
174and other "declared but never defined" types more accurately. However as of
136023e0
XL
175Rust 2018, [the feature is stuck in limbo over how `size_of_val::<MyExternType>()`
176should behave][extern-types-issue].
c1a9b12d 177
450edc1f 178[extern-types]: https://github.com/rust-lang/rfcs/blob/master/text/1861-extern-types.md
136023e0 179[extern-types-issue]: https://github.com/rust-lang/rust/issues/43467
29967ef6
XL
180[`str`]: ../std/primitive.str.html
181[slice]: ../std/primitive.slice.html