Skip to content

Commit 66b4d58

Browse files
authored
Document the enum changes in RFC 2195 (#879)
Document the changes introduced in https://github.com/rust-lang/rfcs/blob/master/text/2195-really-tagged-unions.md. Fixes #244 (I believe), fixes #786.
1 parent 25391db commit 66b4d58

File tree

1 file changed

+200
-21
lines changed

1 file changed

+200
-21
lines changed

src/type-layout.md

+200-21
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,8 @@ layout such as reinterpreting values as a different type.
174174
Because of this dual purpose, it is possible to create types that are not useful
175175
for interfacing with the C programming language.
176176

177-
This representation can be applied to structs, unions, and enums.
177+
This representation can be applied to structs, unions, and enums. The exception
178+
is [zero-variant enums] for which the `C` representation is an error.
178179

179180
#### \#[repr(C)] Structs
180181

@@ -273,9 +274,9 @@ assert_eq!(std::mem::size_of::<SizeRoundedUp>(), 8); // Size of 6 from b,
273274
assert_eq!(std::mem::align_of::<SizeRoundedUp>(), 4); // From a
274275
```
275276

276-
#### \#[repr(C)] Enums
277+
#### \#[repr(C)] Field-less Enums
277278

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

281282
> Note: The enum representation in C is implementation defined, so this is
@@ -285,40 +286,216 @@ the default `enum` size and alignment for the target platform's C ABI.
285286
<div class="warning">
286287

287288
Warning: There are crucial differences between an `enum` in the C language and
288-
Rust's C-like enumerations with this representation. An `enum` in C is
289+
Rust's [field-less enums] with this representation. An `enum` in C is
289290
mostly a `typedef` plus some named constants; in other words, an object of an
290291
`enum` type can hold any integer value. For example, this is often used for
291-
bitflags in `C`. In contrast, Rust’s C-like enumerations can only legally hold
292-
the discriminant values, everything else is undefined behaviour. Therefore,
293-
using a C-like enumeration in FFI to model a C `enum` is often wrong.
292+
bitflags in `C`. In contrast, Rust’s [field-less enums] can only legally hold
293+
the discrimnant values, everything else is [undefined behavior]. Therefore,
294+
using a field-less enum in FFI to model a C `enum` is often wrong.
294295

295296
</div>
296297

297-
It is an error for [zero-variant enumerations] to have the `C` representation.
298+
#### \#[repr(C)] Enums With Fields
298299

299-
For all other enumerations, the layout is unspecified.
300+
The representation of a `repr(C)` enum with fields is a `repr(C)` struct with
301+
two fields, also called a "tagged union" in C:
300302

301-
Likewise, combining the `C` representation with a primitive representation, the
302-
layout is unspecified.
303+
- a `repr(C)` version of the enum with all fields removed ("the tag")
304+
- a `repr(C)` union of `repr(C)` structs for the fields of each variant that had
305+
them ("the payload")
306+
307+
> Note: Due to the representation of `repr(C)` structs and unions, if a variant
308+
> has a single field there is no difference between putting that field directly
309+
> in the union or wrapping it in a struct; any system which wishes to manipulate
310+
> such an `enum`'s representation may therefore use whichever form is more
311+
> convenient or consistent for them.
312+
313+
```rust
314+
// This Enum has the same representation as ...
315+
#[repr(C)]
316+
enum MyEnum {
317+
A(u32),
318+
B(f32, u64),
319+
C { x: u32, y: u8 },
320+
D,
321+
}
322+
323+
// ... this struct.
324+
#[repr(C)]
325+
struct MyEnumRepr {
326+
tag: MyEnumDiscriminant,
327+
payload: MyEnumFields,
328+
}
329+
330+
// This is the discriminant enum.
331+
#[repr(C)]
332+
enum MyEnumDiscriminant { A, B, C, D }
333+
334+
// This is the variant union.
335+
#[repr(C)]
336+
union MyEnumFields {
337+
A: MyAFields,
338+
B: MyBFields,
339+
C: MyCFields,
340+
D: MyDFields,
341+
}
342+
343+
#[repr(C)]
344+
#[derive(Copy, Clone)]
345+
struct MyAFields(u32);
346+
347+
#[repr(C)]
348+
#[derive(Copy, Clone)]
349+
struct MyBFields(f32, u64);
350+
351+
#[repr(C)]
352+
#[derive(Copy, Clone)]
353+
struct MyCFields { x: u32, y: u8 }
354+
355+
// This struct could be omitted (it is a zero-sized type), and it must be in
356+
// C/C++ headers.
357+
#[repr(C)]
358+
#[derive(Copy, Clone)]
359+
struct MyDFields;
360+
```
361+
362+
> Note: `union`s with non-`Copy` fields are unstable, see [55149].
303363
304364
### Primitive representations
305365

306366
The *primitive representations* are the representations with the same names as
307367
the primitive integer types. That is: `u8`, `u16`, `u32`, `u64`, `u128`,
308368
`usize`, `i8`, `i16`, `i32`, `i64`, `i128`, and `isize`.
309369

310-
Primitive representations can only be applied to enumerations.
370+
Primitive representations can only be applied to enumerations and have
371+
different behavior whether the enum has fields or no fields. It is an error
372+
for [zero-variant enumerations] to have a primitive representation. Combining
373+
two primitive representations together is an error.
374+
375+
#### Primitive Representation of Field-less Enums
376+
377+
For [field-less enums], primitive representations set the size and alignment to
378+
be the same as the primitive type of the same name. For example, a field-less
379+
enum with a `u8` representation can only have discriminants between 0 and 255
380+
inclusive.
311381

312-
For [C-like enumerations], they set the size and alignment to be the same as the
313-
primitive type of the same name. For example, a C-like enumeration with a `u8`
314-
representation can only have discriminants between 0 and 255 inclusive.
382+
#### Primitive Representation of Enums With Fields
315383

316-
It is an error for [zero-variant enumerations] to have a primitive
317-
representation.
384+
The representation of a primitive representation enum is a `repr(C)` union of
385+
`repr(C)` structs for each variant with a field. The first field of each struct
386+
in the union is the primitive representation version of the enum with all fields
387+
removed ("the tag") and the remaining fields are the fields of that variant.
318388

319-
For all other enumerations, the layout is unspecified.
389+
> Note: This representation is unchanged if the tag is given its own member in
390+
> the union, should that make manipulation more clear for you (although to
391+
> follow the C++ standard the tag member should be wrapped in a `struct`).
392+
393+
```rust
394+
// This enum has the same representation as ...
395+
#[repr(u8)]
396+
enum MyEnum {
397+
A(u32),
398+
B(f32, u64),
399+
C { x: u32, y: u8 },
400+
D,
401+
}
402+
403+
// ... this union.
404+
#[repr(C)]
405+
union MyEnumRepr {
406+
A: MyVariantA,
407+
B: MyVariantB,
408+
C: MyVariantC,
409+
D: MyVariantD,
410+
}
411+
412+
// This is the discriminant enum.
413+
#[repr(u8)]
414+
#[derive(Copy, Clone)]
415+
enum MyEnumDiscriminant { A, B, C, D }
416+
417+
#[repr(C)]
418+
#[derive(Clone, Copy)]
419+
struct MyVariantA(MyEnumDiscriminant, u32);
420+
421+
#[repr(C)]
422+
#[derive(Clone, Copy)]
423+
struct MyVariantB(MyEnumDiscriminant, f32, u64);
424+
425+
#[repr(C)]
426+
#[derive(Clone, Copy)]
427+
struct MyVariantC { tag: MyEnumDiscriminant, x: u32, y: u8 }
428+
429+
#[repr(C)]
430+
#[derive(Clone, Copy)]
431+
struct MyVariantD(MyEnumDiscriminant);
432+
```
433+
434+
> Note: `union`s with non-`Copy` fields are unstable, see [55149].
435+
436+
#### Combining primitive representations of enums with fields and \#[repr(C)]
437+
438+
For enums with fields, it is also possible to combine `repr(C)` and a
439+
primitive representation (e.g., `repr(C, u8)`). This modifies the [`repr(C)`] by
440+
changing the representation of the discriminant enum to the chosen primitive
441+
instead. So, if you chose the `u8` representation, then the discriminant enum
442+
would have a size and alignment of 1 byte.
443+
444+
The discriminant enum from the example [earlier][`repr(C)`] then becomes:
445+
446+
```rust
447+
#[repr(C, u8)] // `u8` was added
448+
enum MyEnum {
449+
A(u32),
450+
B(f32, u64),
451+
C { x: u32, y: u8 },
452+
D,
453+
}
454+
455+
// ...
456+
457+
#[repr(u8)] // So `u8` is used here instead of `C`
458+
enum MyEnumDiscriminant { A, B, C, D }
459+
460+
// ...
461+
```
462+
463+
For example, with a `repr(C, u8)` enum it is not possible to have 257 unique
464+
discriminants ("tags") whereas the same enum with only a `repr(C)` attribute
465+
will compile without any problems.
466+
467+
Using a primitive representation in addition to `repr(C)` can change the size of
468+
an enum from the `repr(C)` form:
469+
470+
```rust
471+
#[repr(C)]
472+
enum EnumC {
473+
Variant0(u8),
474+
Variant1,
475+
}
476+
477+
#[repr(C, u8)]
478+
enum Enum8 {
479+
Variant0(u8),
480+
Variant1,
481+
}
482+
483+
#[repr(C, u16)]
484+
enum Enum16 {
485+
Variant0(u8),
486+
Variant1,
487+
}
488+
489+
// The size of the C representation is platform dependant
490+
assert_eq!(std::mem::size_of::<EnumC>(), 8);
491+
// One byte for the discriminant and one byte for the value in Enum8::Variant0
492+
assert_eq!(std::mem::size_of::<Enum8>(), 2);
493+
// Two bytes for the discriminant and one byte for the value in Enum16::Variant0
494+
// plus one byte of padding.
495+
assert_eq!(std::mem::size_of::<Enum16>(), 4);
496+
```
320497

321-
Likewise, combining two primitive representations together is unspecified.
498+
[`repr(C)`]: #reprc-enums-with-fields
322499

323500
### The alignment modifiers
324501

@@ -379,12 +556,14 @@ used with any other representation.
379556
[`align_of`]: ../std/mem/fn.align_of.html
380557
[`size_of`]: ../std/mem/fn.size_of.html
381558
[`Sized`]: ../std/marker/trait.Sized.html
559+
[`Copy`]: ../std/marker/trait.Copy.html
382560
[dynamically sized types]: dynamically-sized-types.md
383-
[C-like enumerations]: items/enumerations.md#custom-discriminant-values-for-fieldless-enumerations
561+
[field-less enums]: items/enumerations.md#custom-discriminant-values-for-fieldless-enumerations
384562
[enumerations]: items/enumerations.md
385-
[zero-variant enumerations]: items/enumerations.md#zero-variant-enums
563+
[zero-variant enums]: items/enumerations.md#zero-variant-enums
386564
[undefined behavior]: behavior-considered-undefined.md
387565
[27060]: https://github.com/rust-lang/rust/issues/27060
566+
[55149]: https://github.com/rust-lang/rust/issues/55149
388567
[`PhantomData<T>`]: special-types-and-traits.md#phantomdatat
389568
[Default]: #the-default-representation
390569
[`C`]: #the-c-representation

0 commit comments

Comments
 (0)