Skip to content

Commit ad4342b

Browse files
committed
Document new type representations
1 parent 708d8cd commit ad4342b

File tree

1 file changed

+105
-14
lines changed

1 file changed

+105
-14
lines changed

src/type-layout.md

Lines changed: 105 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -224,27 +224,68 @@ assert_eq!(std::mem::align_of::<SizeRoundedUp>(), 4); // From a
224224

225225
#### \#[repr(C)] Enums
226226

227-
For [C-like enumerations], the `C` representation has the size and alignment of
227+
For [field-less enums], the `C` representation has the size and alignment of
228228
the default `enum` size and alignment for the target platform's C ABI.
229229

230230
> Note: The enum representation in C is implementation defined, so this is
231231
> really a "best guess". In particular, this may be incorrect when the C code
232232
> of interest is compiled with certain flags.
233233
234234
> Warning: There are crucial differences between an `enum` in the C language and
235-
> Rust's C-like enumerations with this representation. An `enum` in C is
235+
> Rust's field-less enumerations with this representation. An `enum` in C is
236236
> mostly a `typedef` plus some named constants; in other words, an object of an
237237
> `enum` type can hold any integer value. For example, this is often used for
238-
> bitflags in `C`. In contrast, Rust’s C-like enumerations can only legally hold
238+
> bitflags in `C`. In contrast, Rust’s field-less enums can only legally hold
239239
> the discrimnant values, everything else is undefined behaviour. Therefore,
240-
> using a C-like enumeration in FFI to model a C `enum` is often wrong.
240+
> using a field-less enum in FFI to model a C `enum` is often wrong.
241241
242-
It is an error for [zero-variant enumerations] to have the `C` representation.
242+
For enums with fields, the `C` representation has the same representation as
243+
it would with the [primitive representation] with the field-less enum in its
244+
description having the `C` representation.
243245

244-
For all other enumerations, the layout is unspecified.
246+
```rust
247+
// This Enum has the same layout as
248+
#[repr(C)]
249+
enum MyEnum {
250+
A(u32),
251+
B(f32, u64),
252+
C { x: u32, y: u8 },
253+
D,
254+
}
245255

246-
Likewise, combining the `C` representation with a primitive representation, the
247-
layout is unspecified.
256+
// this struct.
257+
#[repr(C)]
258+
struct MyEnumRepr {
259+
tag: MyEnumTag,
260+
payload: MyEnumPayload,
261+
}
262+
263+
#[repr(C)]
264+
enum MyEnumTag { A, B, C, D }
265+
266+
#[repr(C)]
267+
union MyEnumPayload {
268+
A: u32,
269+
B: MyEnumPayloadB,
270+
C: MyEnumPayloadC,
271+
D: (),
272+
}
273+
274+
#[repr(C)]
275+
struct MyEnumPayloadB(f32, u64);
276+
277+
#[repr(C)]
278+
struct MyEnumPayloadC { x: u32, y: u8 }
279+
```
280+
281+
It is an error for [zero-variant enumerations] to have the `C` representation.
282+
283+
<span id="c-primitive-representation">Combining the `C` representation and a
284+
primitive representation is only defined for enums with fields and it changes
285+
the representation of the tag, e.g. `MyEnumTag` in the previous example, to have
286+
the representation of the chosen primitive representation. So, if you chose the
287+
`u8` representation, then the tag would have a size and alignment of 1 byte.
288+
</span>
248289

249290
### Primitive representations
250291

@@ -254,16 +295,65 @@ the primitive integer types. That is: `u8`, `u16`, `u32`, `u64`, `usize`, `i8`,
254295

255296
Primitive representations can only be applied to enumerations.
256297

257-
For [C-like enumerations], they set the size and alignment to be the same as the
258-
primitive type of the same name. For example, a C-like enumeration with a `u8`
259-
representation can only have discriminants between 0 and 255 inclusive.
298+
For [field-less enums], they set the size and alignment to be the same as
299+
the primitive type of the same name. For example, a field-less enum with
300+
a `u8` representation can only have discriminants between 0 and 255 inclusive.
301+
302+
For enums with fields, the enum will have the same type layout a union with the
303+
`C` representation that's fields consist of structs with the `C` representation
304+
corresponding to each variant in the enum. The first field in each struct is
305+
the same field-less enum with the same primitive representation that is
306+
the enum with all fields in its variants removed and the rest of the fields
307+
consisting of the fields of the corresponding variant in the order defined in
308+
original enumeration.
309+
310+
> Note: This is commonly different than what is done in C and C++. Projects in
311+
> those languages often use a tuple of `(enum, payload)`. For making your enum
312+
> represented like that, see [the tagged union representation] below.
313+
314+
```rust
315+
// This custom enum
316+
#[repr(u8)]
317+
enum MyEnum {
318+
A(u32),
319+
B(f32, u64),
320+
C { x: u32, y: u8 },
321+
D,
322+
}
323+
324+
// has the same type layout as this union
325+
#[repr(C)]
326+
union MyEnumRepr {
327+
A: MyEnumVariantA,
328+
B: MyEnumVariantB,
329+
C: MyEnumVariantC,
330+
D: MyEnumVariantD,
331+
}
332+
333+
#[repr(u8)]
334+
enum MyEnumDiscriminant { A, B, C, D }
335+
336+
#[repr(C)]
337+
struct MyEnumVariantA(MyEnumDiscriminant, u32);
338+
339+
#[repr(C)]
340+
struct MyEnumVariantB(MyEnumDiscriminant, f32, u64);
341+
342+
#[repr(C)]
343+
struct MyEnumVariantC { tag: MyEnumDiscriminant, x: u32, y: u8 }
344+
345+
#[repr(C)]
346+
struct MyEnumVariantD(MyEnumDiscriminant);
347+
```
260348

261349
It is an error for [zero-variant enumerations] to have a primitive
262350
representation.
263351

264-
For all other enumerations, the layout is unspecified.
352+
Combining two primitive representations together is unspecified.
353+
354+
Combining the `C` representation and a primitive representation is described
355+
[above][#c-primitive-representation].
265356

266-
Likewise, combining two primitive representations together is unspecified.
267357

268358
### The `align` Representation
269359

@@ -298,7 +388,8 @@ a `packed` type cannot transitively contain another `align`ed type.
298388
[`size_of`]: ../std/mem/fn.size_of.html
299389
[`Sized`]: ../std/marker/trait.Sized.html
300390
[dynamically sized types]: dynamically-sized-types.html
301-
[C-like enumerations]: items/enumerations.html#custom-discriminant-values-for-field-less-enumerations
391+
[field-less enums]: items/enumerations.html#custom-discriminant-values-for-field-less-enumerations
302392
[zero-variant enumerations]: items/enumerations.html#zero-variant-enums
303393
[undefined behavior]: behavior-considered-undefined.html
304394
[27060]: https://github.com/rust-lang/rust/issues/27060
395+
[primitive representation]: #primitive-representations

0 commit comments

Comments
 (0)