Skip to content

Commit 824a59f

Browse files
committed
Guarantee layout of some single variant enums
Addresses part of #79 .
1 parent 003eee9 commit 824a59f

File tree

1 file changed

+45
-63
lines changed

1 file changed

+45
-63
lines changed

Diff for: reference/src/layout/enums.md

+45-63
Original file line numberDiff line numberDiff line change
@@ -378,90 +378,72 @@ enum Enum2<T> {
378378

379379
[niche]: ../glossary.html#niche
380380

381-
## Unresolved questions
382-
383381
### Layout of single variant enums
384382

385-
[Issue #79.](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/79)
386-
387-
Enums that contain a **single variant** and which do not have an
388-
explicit `#[repr]` annotation are an important special case. Since
389-
there is only a single variant, the enum must be instantiated with
390-
that variant, which means that the enum is in fact equivalent to a
391-
struct. The question then is to what extent we should **guarantee**
392-
that the two share an equivalent layout, and also how to define the
393-
interaction with uninhabited types.
383+
**Single variant data-carrying*** enums without a `repr()` annotation have the
384+
same layout as the variant field. **Single variant fieldless** enums have the
385+
same layout as a unit struct.
394386

395-
As presently implemented, the compiler will use the same layout for
396-
structs and for single variant enums (as long as they do not have a
397-
`#[repr]` annotation that overrides that choice). So, for example, the
398-
struct `SomeStruct` and the enum `SomeEnum` would have an equivalent
399-
layout ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3697ac684c3d021892694956df957653)):
387+
Here:
400388

401389
```rust
402-
struct SomeStruct;
403-
enum SomeEnum {
404-
SomeVariant,
405-
}
406-
390+
# use std::mem::{size_of, align_of};
391+
struct UnitStruct;
392+
enum SingleVariantFieldless { FieldlessVariant }
407393
# fn main() {
408-
# use std::mem;
409-
let x = SomeStruct;
410-
let y = SomeEnum::SomeVariant;
411-
assert_eq!(
412-
mem::size_of_val(&x),
413-
mem::size_of_val(&y),
414-
"types have different sizes",
415-
);
416-
println!("types both have size {}", std::mem::size_of_val(&x));
394+
assert_eq!(size_of::<SingleVariantFieldless>(), size_of::<UnitStruct>());
395+
assert_eq!(align_of::<SingleVariantFieldless>(), align_of::<UnitStruct>());
396+
// assert_eq!(call_abi_of::<SingleVariantFieldless>(), call_abi_of::<UnitStruct>());
397+
// assert_eq!(niches_of::<SingleVariantFieldless>(), niches_of::<UnitStruct>());
417398
# }
418399
```
419400

420-
Similarly, the struct `SomeStruct` and the enum `SomeVariant` in this
421-
example would also be equivalent in their layout
422-
([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=924724764419f846c788a8763da45992)):
401+
the single-variant fieldless enum `SingleVariantFieldless` has the same layout
402+
as `UnitStruct`.
403+
404+
Here:
423405

424406
```rust
407+
# use std::mem::{size_of, align_of};
425408
struct SomeStruct { x: u32 }
426-
enum SomeEnum {
427-
SomeVariant { x: u32 },
409+
enum SingleVariantDataCarrying {
410+
DataCarryingVariant(SomeStruct),
428411
}
429-
430412
# fn main() {
431-
# use std::mem;
432-
let x = SomeStruct { x: 22 };
433-
let y = SomeEnum::SomeVariant { x: 22 };
434-
assert_eq!(
435-
mem::size_of_val(&x),
436-
mem::size_of_val(&y),
437-
"types have different sizes",
438-
);
439-
println!("types both have size {}", mem::size_of_val(&x));
440-
}
413+
# assert_eq!(size_of::<SingleVariantDataCarrying>(), size_of::<SomeStruct>());
414+
# assert_eq!(align_of::<SingleVariantDataCarrying>(), align_of::<SomeStruct>());
415+
# }
441416
```
442417

443-
In fact, the compiler will use this optimized layout even for enums
444-
that define multiple variants, as long as all but one of the variants
445-
is uninhabited
446-
([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3cc1484c5b91097f3dc2015b7c207a0e)):
418+
the single-variant data-carrying enum `SingleVariantDataCarrying` has the same
419+
layout as `SomeStruct`.
420+
421+
### Layout of multi-variant enums with one inhabited variant
422+
423+
The layout of **multi-variant** enums with **one inhabited variant** is the same
424+
as that of the single-variant enum containing that same inhabited variant.
425+
426+
Here:
447427

448428
```rust
449-
# enum Void { }
429+
# use std::mem::{size_of, align_of};
450430
struct SomeStruct { x: u32 }
451-
enum SomeEnum {
452-
SomeVariant { x: u32 },
453-
UninhabitedVariant { y: Void },
431+
enum SingleVariantDataCarrying {
432+
DataCarryingVariant(SomeStruct),
433+
}
434+
enum Void0 {}
435+
enum Void1 {}
436+
enum MultiVariantSingleHabited {
437+
DataCarryingVariant(SomeStruct),
438+
Uninhabited0(Void0),
439+
Uninhabited1(Void1),
454440
}
455-
456441
# fn main() {
457442
# use std::mem;
458-
let x = SomeStruct { x: 22 };
459-
let y = SomeEnum::SomeVariant { x: 22 };
460-
assert_eq!(
461-
mem::size_of_val(&x),
462-
mem::size_of_val(&y),
463-
"types have different sizes",
464-
);
465-
println!("types both have size {}", mem::size_of_val(&x));
443+
# assert_eq!(size_of::<SingleVariantDataCarrying>(), size_of::<MultiVariantSingleHabited>());
444+
# assert_eq!(align_of::<SingleVariantDataCarrying>(), align_of::<MultiVariantSingleHabited>());
466445
# }
467446
```
447+
448+
the `MultiVariantSingleHabited` enum has the same layout as
449+
`SingleVariantDataCarrying`.

0 commit comments

Comments
 (0)