@@ -6,29 +6,107 @@ not to change until an RFC ratifies them.
6
6
7
7
[ #13 ] : https://github.com/rust-rfcs/unsafe-code-guidelines/issues/13
8
8
9
- The only degree of freedom the compiler has when computing the layout of a union
10
- like
9
+ ### Layout of individual union fields
11
10
12
- ``` rust,ignore
13
- union U { f1: T1, f2: T2 }
11
+ A union consists of several variants, one for each field. All variants have the
12
+ same size and start at the same memory address, such that in memory the variants
13
+ overlap. This can be visualized as follows:
14
+
15
+ ``` text
16
+ [ <--> [field0_ty] <----> ]
17
+ [ <----> [field1_ty] <--> ]
18
+ [ <---> [field2_ty] <---> ]
14
19
```
20
+ ** Figure 1** (union-field layout): Each row in the picture shows the layout of
21
+ the union for each of its variants. The ` <-...-> ` and ` [ ... ] ` denote the
22
+ differently-sized gaps and fields, respectively.
23
+
24
+ The individual fields (` [field{i}_ty_] ` ) are blocks of fixed size determined by
25
+ the field's [ layout] . Since we allow creating references to union fields
26
+ (` &u.i ` ), the only degrees of freedom the compiler has when computing the layout
27
+ of a union are the size of the union, which can be larger than the size of its
28
+ largest field, and the offset of each union field within its variant. How these
29
+ are picked depends on certain constraints like, for example, the alignment
30
+ requirements of the fields, the ` #[repr] ` attribute of the ` union ` , etc.
31
+
32
+ [ padding ] : ../glossary.md#padding
33
+ [ layout ] : ../glossary.md#layout
34
+
35
+ ### Unions with default layout ("` repr(Rust) ` ")
15
36
16
- is to determine the offset of the fields. The layout of these fields themselves
17
- is already entirely determined by their types, and since we intend to allow
18
- creating references to fields (` &u.f1 ` ), unions do not have any wiggle-room
19
- there.
37
+ Except for the guarantees provided below for some specific cases, the default
38
+ layout of Rust unions is, _ in general_ , ** unspecified** .
20
39
21
- ### Default layout ("repr rust")
40
+ That is, there are no _ general_ guarantees about the offset of the fields,
41
+ whether all fields have the same offset, what the call ABI of the union is, etc.
22
42
23
- ** The default layout of unions is not specified.** As of this writing, we want
24
- to keep the option of using non-zero offsets open for the future; whether this
25
- is useful depends on what exactly the compiler-assumed invariants about union
26
- contents are.
43
+ <details ><summary ><b >Rationale</b ></summary >
44
+
45
+ As of this writing, we want to keep the option of using non-zero offsets open
46
+ for the future; whether this is useful depends on what exactly the
47
+ compiler-assumed invariants about union contents are. This might become clearer
48
+ after the [ validity of unions] [ #73 ] is settled.
27
49
28
50
Even if the offsets happen to be all 0, there might still be differences in the
29
51
function call ABI. If you need to pass unions by-value across an FFI boundary,
30
52
you have to use ` #[repr(C)] ` .
31
53
54
+ [ #73 ] : https://github.com/rust-lang/unsafe-code-guidelines/issues/73
55
+
56
+ </details >
57
+
58
+ #### Layout of unions with a single non-zero-sized field
59
+
60
+ The layout of unions with a single non-[ 1-ZST] -field" is the same as the
61
+ layout of that field if it has no [ padding] bytes.
62
+
63
+ For example, here:
64
+
65
+ ``` rust
66
+ # use std :: mem :: {size_of, align_of};
67
+ # #[derive(Copy , Clone )]
68
+ #[repr(transparent)]
69
+ struct SomeStruct (i32 );
70
+ # #[derive(Copy , Clone )]
71
+ struct Zst ;
72
+ union U0 {
73
+ f0 : SomeStruct ,
74
+ f1 : Zst ,
75
+ }
76
+ # fn main () {
77
+ # assert_eq! (size_of :: <U0 >(), size_of :: <SomeStruct >());
78
+ # assert_eq! (align_of :: <U0 >(), align_of :: <SomeStruct >());
79
+ # }
80
+ ```
81
+
82
+ the union ` U0 ` has the same layout as ` SomeStruct ` , because ` SomeStruct ` has no
83
+ padding bits - it is equivalent to an ` i32 ` due to ` repr(transparent) ` - and
84
+ because ` Zst ` is a [ 1-ZST] .
85
+
86
+ On the other hand, here:
87
+
88
+ ``` rust
89
+ # use std :: mem :: {size_of, align_of};
90
+ # #[derive(Copy , Clone )]
91
+ struct SomeOtherStruct (i32 );
92
+ # #[derive(Copy , Clone )]
93
+ #[repr(align(16))] struct Zst2 ;
94
+ union U1 {
95
+ f0 : SomeOtherStruct ,
96
+ f1 : Zst2 ,
97
+ }
98
+ # fn main () {
99
+ # assert_eq! (size_of :: <U1 >(), align_of :: <Zst2 >());
100
+ # assert_eq! (align_of :: <U1 >(), align_of :: <Zst2 >());
101
+ assert_eq! (align_of :: <Zst2 >(), 16 );
102
+ # }
103
+ ```
104
+
105
+ the layout of ` U1 ` is ** unspecified** because:
106
+
107
+ * ` Zst2 ` is not a [ 1-ZST] , and
108
+ * ` SomeOtherStruct ` has an unspecified layout and could contain padding bytes.
109
+
32
110
### C-compatible layout ("repr C")
33
111
34
112
The layout of ` repr(C) ` unions follows the C layout scheme. Per sections
@@ -93,3 +171,5 @@ with no fields. When such types are used as an union field in C++, a "naive"
93
171
translation of that code into Rust will not produce a compatible result. Refer
94
172
to the [ struct chapter] ( structs-and-tuples.md#c-compatible-layout-repr-c ) for
95
173
further details.
174
+
175
+ [ 1-ZST ] : ../glossary.md#zero-sized-type--zst
0 commit comments